<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>OnMCU - Notes from the board farm</title>
      <link>https://www.onmcu.com/blog/</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://www.onmcu.com/blog/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Tue, 18 Aug 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>On-target unit tests with defmt-test: from cargo test to real silicon</title>
          <pubDate>Tue, 14 Jul 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.onmcu.com/blog/on-target-unit-tests-with-defmt-test/</link>
          <guid>https://www.onmcu.com/blog/on-target-unit-tests-with-defmt-test/</guid>
          <description xml:base="https://www.onmcu.com/blog/on-target-unit-tests-with-defmt-test/">&lt;p&gt;Have you ever wondered why unit tests are never run on MCUs?&lt;/p&gt;
&lt;p&gt;Most embedded projects do have some form of testing, but those tests usually run in one of two
places.&lt;/p&gt;
&lt;p&gt;Some are unit tests run on the host. This is fast and works well for parsers, state machines,
calculations, and other code that does not depend on the target hardware. Also, it’s easy to
integrate into CI.&lt;/p&gt;
&lt;p&gt;The rest are manual tests run on a board connected to a developer’s machine. Those tests are closer
to the real system, but they are often difficult to run automatically.&lt;/p&gt;
&lt;p&gt;Both are useful, but they leave a gap. Host-side tests do not run in the production environment,
while boards on developers’ desks are usually difficult for CI to reach.&lt;/p&gt;
&lt;p&gt;On-target unit tests close that gap and are useful because they sit between those two approaches.
They are small tests that run on the actual microcontroller and report success or failure like any
other automated test suite. With OnMCU, they can run in CI without requiring you to maintain a board
farm or self-hosted runner.&lt;/p&gt;
&lt;p&gt;In this article, we will describe that workflow using three components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;defmt-test&lt;/code&gt; runs the test suite on the microcontroller.&lt;/li&gt;
&lt;li&gt;Semihosting sends the final pass or fail status through the debug connection.&lt;/li&gt;
&lt;li&gt;OnMCU provides the physical board and propagates that status back to Cargo and CI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The test itself is only one part of the problem, though. Compiling and flashing a test binary is
straightforward. The more interesting question is how the microcontroller tells the host that the
test has finished, and whether it passed. That is part of the “test harness” surrounding and enabling
our tests. The most common way how this technical challenge is solved in practice is called
semihosting, which provides that missing connection so in order to explain the whole process, we will
start there.&lt;/p&gt;
&lt;h2 id=&quot;returning-an-exit-status-with-semihosting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#returning-an-exit-status-with-semihosting&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Returning an exit status with semihosting&lt;/h2&gt;
&lt;p&gt;On a system with an operating system, a program can simply return an exit status. That status is an
integer passed to the parent process, often a shell, so that it knows what happened.&lt;/p&gt;
&lt;p&gt;By convention, an exit status of &lt;code&gt;0&lt;/code&gt; indicates success. Any other value indicates some kind of error.&lt;/p&gt;
&lt;p&gt;A CI runner uses exactly this mechanism. It starts a process, waits for it to finish, and marks the
step as successful or failed based on the process exit status.&lt;/p&gt;
&lt;p&gt;A bare-metal microcontroller has no parent process to return to. Nevertheless, we can achieve nearly
the same behaviour using semihosting and the flashing tools we already use.&lt;/p&gt;
&lt;p&gt;Before looking at how &lt;code&gt;defmt-test&lt;/code&gt; uses this, it is worth understanding what semihosting actually
does.&lt;/p&gt;
&lt;h2 id=&quot;how-semihosting-works&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-semihosting-works&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;How semihosting works&lt;/h2&gt;
&lt;p&gt;Semihosting is a mechanism that allows code running on an embedded target to use facilities provided
by a host computer. For this to work, the host must have a debugger attached to the target.&lt;/p&gt;
&lt;p&gt;The target makes a semihosting request by placing information in known registers or memory locations
and then halting the processor with a specially shaped instruction. That information tells the
debugger which operation to perform and where to find its arguments.&lt;/p&gt;
&lt;p&gt;The requested operation might be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;printing text to the host console,&lt;/li&gt;
&lt;li&gt;accessing a file on the host,&lt;/li&gt;
&lt;li&gt;reading input,&lt;/li&gt;
&lt;li&gt;or terminating the program with an exit status.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The debugger detects the halt, reads the request and its arguments, performs the operation on the
host, and then resumes the target when appropriate. If the operation returns data, the debugger makes
that data available to the firmware before execution continues.&lt;/p&gt;
&lt;p&gt;Arm introduced and specified semihosting roughly 30 years ago, and it is still widely used today. It
is also available on architectures other than Arm, including RISC-V. The current upstream
specification is maintained in the
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst&quot;&gt;Arm ABI repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For on-target tests, the operation we care about is program termination.&lt;/p&gt;
&lt;h2 id=&quot;semihosting-on-cortex-m&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#semihosting-on-cortex-m&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Semihosting on Cortex-M&lt;/h2&gt;
&lt;p&gt;The instruction used to enter semihosting depends on the target architecture. Cortex-A, Cortex-R, and
Cortex-M use different mechanisms, including breakpoint, halt, and supervisor-call instructions.&lt;/p&gt;
&lt;p&gt;Most microcontrollers based on Arm cores use Cortex-M. On these targets, a semihosting request is
normally made with a breakpoint with a specific immediate value:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;bkpt &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;0xAB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces the opcode &lt;code&gt;0xBEAB&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before executing the instruction, the firmware places the semihosting operation in register &lt;code&gt;r0&lt;/code&gt; and
a pointer to its argument block in register &lt;code&gt;r1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To return an exit status, we place the &lt;code&gt;SYS_EXIT_EXTENDED&lt;/code&gt; (value &lt;code&gt;0x20&lt;/code&gt;) into &lt;code&gt;r0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Its argument is a two-word block:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The reason why execution stopped&lt;/li&gt;
&lt;li&gt;An application-defined subcode&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The usual reason is &lt;code&gt;ADP_Stopped_ApplicationExit&lt;/code&gt;, with the value &lt;code&gt;0x20026&lt;/code&gt;. When that reason is
used, the second word contains the program’s exit status.&lt;/p&gt;
&lt;p&gt;Another possible reason is &lt;code&gt;ADP_Stopped_InternalError&lt;/code&gt;, with the value &lt;code&gt;0x20024&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A minimal C implementation for a Cortex-M target looks like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;include&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;stdint.h&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; Semihosting operations; see the Arm semihosting specification.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;define&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; SYS_EXIT_EXTENDED&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;20&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;define&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; ADP_STOPPED_APPLICATION_EXIT&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;20026&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;u&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; Issue a semihosting call. The operation is passed in r0 and the parameter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; block in r1. The debugger detects the `bkpt 0xAB` instruction.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; int&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; semihost_call&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; op&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;arg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    register&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; int&lt;/span&gt;&lt;span&gt; r0 &lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;__asm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;r0&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; op&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    register&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; void&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; *&lt;/span&gt;&lt;span&gt;r1 &lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;__asm&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;r1&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; arg&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    __asm &lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;volatile&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;bkpt 0xAB&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        : &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;+r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r0&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        : &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;r1&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        : &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;memory&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    return&lt;/span&gt;&lt;span&gt; r0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; sys_exit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; code&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;    //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; SYS_EXIT_EXTENDED takes a two-word block containing the stop reason&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;    //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; and the exit status.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    uint32_t&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; block&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;2&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ADP_STOPPED_APPLICATION_EXIT&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;uint32_t&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;code&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;    semihost_call&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;SYS_EXIT_EXTENDED&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; block&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;    //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; The host normally terminates the debug session above. Stay here if it&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;    //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; does not.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We use this in our
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/onmcu/c-examples/blob/main/nucleo-h743zi/main.c&quot;&gt;minimal C example for the NUCLEO-H743ZI&lt;/a&gt;
to signal a successful run.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/taiki-e/semihosting/blob/main/src/process.rs&quot;&gt;&lt;code&gt;semihosting&lt;/code&gt; Rust crate&lt;/a&gt;
contains the equivalent implementation for Rust.&lt;/p&gt;
&lt;h2 id=&quot;semihosting-on-risc-v&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#semihosting-on-risc-v&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Semihosting on RISC-V&lt;/h2&gt;
&lt;p&gt;The basic idea is the same on RISC-V, but there is one small difference.&lt;/p&gt;
&lt;p&gt;RISC-V does not have an immediate value on its breakpoint instruction. A debugger therefore cannot
distinguish a semihosting breakpoint from an ordinary breakpoint by looking at the breakpoint
instruction alone.&lt;/p&gt;
&lt;p&gt;To solve this, the &lt;code&gt;ebreak&lt;/code&gt; instruction is wrapped in two specific no-op instructions:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;asm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;slli x0, x0, &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;0x1f&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;  # 0x01f01013: entry NOP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ebreak            &lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; # 0x00100073: break to debugger&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;srai x0, x0, &lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;     # 0x40705013: exit NOP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two shift instructions (&lt;code&gt;slli&lt;/code&gt; for “shift left logical immediate” and &lt;code&gt;srai&lt;/code&gt; for “shift right
arithmetic immediate”) do nothing because they write to &lt;code&gt;x0&lt;/code&gt;, which always remains zero. Their
unusual shift amounts make the sequence easy to recognize and unlikely to appear in normal compiled
code.&lt;/p&gt;
&lt;p&gt;When the debugger encounters the &lt;code&gt;ebreak&lt;/code&gt;, it checks the instructions immediately before and after
it. If they match these encodings, the debugger handles the trap as a semihosting request rather than
a regular breakpoint.&lt;/p&gt;
&lt;p&gt;The details differ slightly, but the result is the same: firmware running without an operating system
can ask the debugger to terminate the host-side process with a particular exit status.&lt;/p&gt;
&lt;p&gt;That leaves one practical question: how does this semihosting status become the exit status of the
command running on the host?&lt;/p&gt;
&lt;h2 id=&quot;from-semihosting-to-the-host-process&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#from-semihosting-to-the-host-process&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;From semihosting to the host process&lt;/h2&gt;
&lt;p&gt;This is the convenient part: the flashing and debugging tools already understand the protocol.&lt;/p&gt;
&lt;p&gt;With OpenOCD, semihosting must be enabled explicitly. A non-interactive invocation suitable for CI
might look like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;openocd&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; interface/stlink.cfg&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; target/stm32h7x.cfg&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;init&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;reset halt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;arm semihosting enable&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;program firmware.elf verify&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;reset halt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;resume&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OpenOCD flashes the binary, resumes the target, and waits for a semihosting exit request. Once it
receives one, it terminates with the corresponding exit status.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;probe-rs run&lt;/code&gt;, semihosting detection is enabled by default. Whenever the core reaches a
breakpoint, &lt;code&gt;probe-rs&lt;/code&gt; checks whether it is part of a semihosting operation.&lt;/p&gt;
&lt;p&gt;Some operations, such as host file access, require explicit permission. &lt;code&gt;SYS_EXIT_EXTENDED&lt;/code&gt;, however,
works without additional configuration.&lt;/p&gt;
&lt;p&gt;The complete path is therefore:&lt;/p&gt;
&lt;figure class=&quot;onmcu-web-figure&quot;&gt;
  &lt;img src=&quot;/images/blog/semihosting-exit-status-path.png&quot;
       alt=&quot;Five stacked layers, from firmware calling SYS_EXIT_EXTENDED on the MCU, through the debug probe detecting the semihosting request over SWD or JTAG, probe-rs or OpenOCD reading the requested exit status, the flashing tool exiting with that status, up to CI marking the step as passed or failed.&quot;
       width=&quot;1392&quot; height=&quot;550&quot; loading=&quot;lazy&quot;&gt;
  &lt;figcaption&gt;Figure 1: Passthrough of the semihosting exit status.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There is no need to search the serial output for words such as &lt;code&gt;PASS&lt;/code&gt; or &lt;code&gt;FAIL&lt;/code&gt;. The result is
carried through the same process exit mechanism that CI already uses for host-side tests.&lt;/p&gt;
&lt;p&gt;Now that we have a reliable way to return a test result, we can look at how &lt;code&gt;defmt-test&lt;/code&gt; builds a
Rust test harness around it.&lt;/p&gt;
&lt;h2 id=&quot;the-test-harness-defmt-test&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-test-harness-defmt-test&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;The test harness: defmt-test&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://crates.io/crates/defmt-test&quot;&gt;&lt;code&gt;defmt-test&lt;/code&gt;&lt;/a&gt; is a small test harness for embedded Rust. It
allows tests to run directly on a device while keeping the familiar structure of Rust’s built-in
&lt;code&gt;#[test]&lt;/code&gt; system.&lt;/p&gt;
&lt;p&gt;To use it, add &lt;code&gt;defmt-test&lt;/code&gt; as a development dependency and annotate the test module with
&lt;code&gt;#[defmt_test::tests]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A minimal test for the NUCLEO-H743ZI might look like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; tests/hardware.rs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;defmt_test&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span&gt;tests&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;mod&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; tests&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    use&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; super&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;*&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; init&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; State&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; defmt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;unwrap!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;cortex_m&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;Peripherals&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;take&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; dp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; defmt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;unwrap!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;pac&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;Peripherals&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;take&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;        //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; Enable the CRC peripheral clock on RCC AHB4.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;RCC&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;ahb4enr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;modify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;crcen&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;set_bit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;        //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; Read the register back to ensure the write has taken effect.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; _&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; dp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;RCC&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;ahb4enr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;read&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;        State&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt; cp&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; dp&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; cpu_is_cortex_m7&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; State&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; cpuid&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;CPUID&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;read&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;        //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; PARTNO 0xC27 identifies a Cortex-M7.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;        defmt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;assert_eq!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cpuid&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; 4&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; 0xFFF&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; 0xC27&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;    fn&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; hardware_crc_matches_software&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;state&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;mut&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt; State&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;u32&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; 4&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;            0x1234_5678&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;            0xDEAD_BEEF&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;            0x0000_0000&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;            0xFFFF_FFFF&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt;        //&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6E7781, #8B949E);&quot;&gt; Reload the CRC data register from INIT.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        state&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span&gt;dp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;CRC&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;cr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;write&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;reset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;set_bit&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        for&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;word&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            state&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;                .&lt;/span&gt;&lt;span&gt;dp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;CRC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;dr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;                .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;write&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span&gt;w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; unsafe&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span&gt; w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;dr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;bits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;word&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;        let&lt;/span&gt;&lt;span&gt; hardware_result&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt; =&lt;/span&gt;&lt;span&gt; state&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span&gt;dp&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;CRC&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;dr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;read&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;bits&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;        defmt&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt;assert_eq!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;hardware_result&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: light-dark(#8250DF, #D2A8FF);&quot;&gt; crc32_mpeg2&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#CF222E, #FF7B72);&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;#[init]&lt;/code&gt; function runs before the tests and returns state that can be passed to each test. In
this example, that state contains the core and device peripherals.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;defmt-test&lt;/code&gt; also provides hooks for code that should run before or after individual tests or the
complete test suite. Its &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://crates.io/crates/defmt-test&quot;&gt;crate documentation&lt;/a&gt; describes the
available attributes.&lt;/p&gt;
&lt;p&gt;During the run, &lt;code&gt;defmt-test&lt;/code&gt; prints the name and result of each test through &lt;code&gt;defmt&lt;/code&gt;. Once all tests
have passed, it terminates the program through semihosting.&lt;/p&gt;
&lt;p&gt;If a test fails, the assertion panics. Together with &lt;code&gt;panic-probe&lt;/code&gt;, that gives us a decoded panic
message with information about the failing assertion.&lt;/p&gt;
&lt;p&gt;The test harness therefore takes care of both sides of the result:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;success -&amp;gt; semihosting exit with status 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;failure -&amp;gt; panic reported through defmt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The debugger sees the result and turns it into the exit status of the host-side process.&lt;/p&gt;
&lt;h2 id=&quot;telling-cargo-how-to-run-the-test&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#telling-cargo-how-to-run-the-test&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Telling Cargo how to run the test&lt;/h2&gt;
&lt;p&gt;Cargo normally builds embedded test files using Rust’s host-oriented test harness. We need to disable
that harness for every test binary that should run on the target.&lt;/p&gt;
&lt;p&gt;For the example above, add the following to &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[[&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;test&lt;/span&gt;&lt;span&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;hardware&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;harness&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt; false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cargo can now build the test as a normal embedded binary. The remaining step is to tell it what to do
with the resulting ELF.&lt;/p&gt;
&lt;p&gt;Cargo uses the target runner configured in &lt;code&gt;.cargo/config.toml&lt;/code&gt; whenever it needs to execute a binary
for that target.&lt;/p&gt;
&lt;p&gt;To run the test on OnMCU, the runner can be configured like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;target&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#953800, #FFA657);&quot;&gt;thumbv7em-none-eabihf&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;runner&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;onmcu run --board NUCLEO-H743ZI --ignore-trailing-args --file&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;NUCLEO-H743ZI&lt;/code&gt; with the board required by your project.&lt;/p&gt;
&lt;p&gt;After that, the usual Cargo command is enough:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cargo test --test hardware&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cargo compiles the test and passes the resulting ELF to OnMCU. OnMCU allocates the requested board,
flashes the binary, and runs it.&lt;/p&gt;
&lt;p&gt;The same runner configuration also works with editor integrations. Pressing the &lt;strong&gt;Run Test&lt;/strong&gt; button
above the test module in VS Code causes rust-analyzer to invoke Cargo, which in turn hands the binary
to the configured runner, in our case OnMCU.&lt;/p&gt;
&lt;p&gt;Nothing in the test itself needs to know whether the board is connected locally or hosted remotely.&lt;/p&gt;
&lt;h2 id=&quot;how-onmcu-closes-the-missing-link&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-onmcu-closes-the-missing-link&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;How OnMCU closes the missing link&lt;/h2&gt;
&lt;p&gt;Without OnMCU, the machine running the Cargo command would need direct access to a development board
and debug probe.&lt;/p&gt;
&lt;p&gt;That is manageable on a developer’s workstation. In CI, it normally means setting up a self-hosted
runner, connecting the board, keeping the runner online, and making sure each test starts from a
known hardware state. When using Docker, it usually requires quite some fiddling around to get USB
data in and out of the container. As a result, even when automated, self-built test setups are often
unreliable and break frequently.&lt;/p&gt;
&lt;p&gt;OnMCU replaces that local setup.&lt;/p&gt;
&lt;p&gt;The CLI uploads the test binary and requests a compatible board. On the OnMCU infrastructure, the
binary is flashed and executed using tools such as &lt;code&gt;probe-rs&lt;/code&gt;. The logs are streamed back to the CLI
while the test runs.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;defmt-test&lt;/code&gt; eventually exits through semihosting, &lt;code&gt;probe-rs&lt;/code&gt; receives the result. OnMCU then
propagates that result through the CLI process.&lt;/p&gt;
&lt;p&gt;From the CI runner’s point of view, it is still an ordinary command:&lt;/p&gt;
&lt;figure class=&quot;onmcu-web-figure&quot;&gt;
  &lt;img src=&quot;/images/blog/exit-status-to-ci-step.png&quot;
       alt=&quot;Exit code 0 leads to a green check mark labelled test step passes; a non-zero status leads to a red cross labelled test step fails.&quot;
       width=&quot;582&quot; height=&quot;207&quot; loading=&quot;lazy&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;The semihosting result therefore travels through every layer without being translated into a separate
reporting format:&lt;/p&gt;
&lt;figure class=&quot;onmcu-web-figure&quot;&gt;
  &lt;img src=&quot;/images/blog/result-chain-target-to-ci.png&quot;
       alt=&quot;A stack split into target and host. On the target, defmt-test reports pass or fail and semihosting sends a SYS_EXIT reason over the debug link. On the host, probe-rs decodes the reason to 0 or 1, the OnMCU CLI normalizes it to 0 for pass and 10 for a test failure, Cargo returns the runner&#39;s status, and the CI runner uses it as the job status.&quot;
       width=&quot;619&quot; height=&quot;559&quot; loading=&quot;lazy&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;That simple chain is what makes the setup fit naturally into existing Rust workflows.&lt;/p&gt;
&lt;h2 id=&quot;running-in-github-actions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#running-in-github-actions&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Running in GitHub Actions&lt;/h2&gt;
&lt;p&gt;To run the firmware in CI, build the ELF and pass it to
&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/onmcu/onmcu-action&quot;&gt;&lt;code&gt;onmcu/onmcu-action&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#1F2328, #E6EDF3); background-color: light-dark(#FFFFFF, #0D1117);&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; H&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;ardware&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0550AE, #79C0FF);&quot;&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;ush&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ermissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;  c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ontents&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;ead&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;j&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;obs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;  r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;un&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;    r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;uns-on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; u&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;buntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;    s&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;teps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt; u&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; a&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;ctions/checkout@v4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; P&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;rovision Rust toolchain and targets&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;        r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;un&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;ustup show&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt; u&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; S&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;watinem/rust-cache@v2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt; n&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ame&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;uild&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;        r&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;un&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; c&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;argo build --release&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt; u&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ses&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; o&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;nmcu/onmcu-action@v1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;        w&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ith&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;          b&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;oard&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; N&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;UCLEO-H743ZI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;          f&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;ile&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; /&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;target/thumbv7em-none-eabihf/release/nucleo-h743zi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;          a&lt;/span&gt;&lt;span style=&quot;color: light-dark(#116329, #7EE787);&quot;&gt;pi-key&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt; $&lt;/span&gt;&lt;span style=&quot;color: light-dark(#0A3069, #A5D6FF);&quot;&gt;{{ secrets.ONMCU_API_KEY }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The action uploads the ELF and runs it on the requested board. The firmware’s semihosting exit status
becomes the result of the GitHub Actions step, so no log parsing is required.&lt;/p&gt;
&lt;h2 id=&quot;not-just-rust&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#not-just-rust&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Not just Rust&lt;/h2&gt;
&lt;p&gt;OnMCU itself is language-agnostic. At the lowest level, the platform flashes a binary, runs it,
collects its output, and determines whether the run succeeded.&lt;/p&gt;
&lt;p&gt;C and C++ projects can use the same model with Unity, CppUTest, or a custom on-target test harness. As
long as the test binary communicates a result that the flashing tool can recognize, OnMCU can pass
that result back to CI.&lt;/p&gt;
&lt;p&gt;Rust is especially convenient because the pieces already fit together.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;defmt-test&lt;/code&gt; provides the test structure. &lt;code&gt;panic-probe&lt;/code&gt; reports failures. &lt;code&gt;probe-rs&lt;/code&gt; handles flashing,
logging, and semihosting. OnMCU supplies the physical board and makes the resulting exit status
available to the CI runner.&lt;/p&gt;
&lt;p&gt;For an existing embedded Rust project, moving a test from a board on a desk to a remotely hosted board
can therefore require little more than changing the Cargo runner. For a C/C++ project, we have an
example Makefile in our &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/onmcu/c-examples/&quot;&gt;C-examples repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;On-target unit tests do not replace host-side tests.&lt;/p&gt;
&lt;p&gt;Code that is independent of the target should usually remain on the host, where tests run faster and
failures are easier to debug. On-target tests are useful for the parts that depend on the processor,
memory layout, peripherals, or interrupt behaviour.&lt;/p&gt;
&lt;p&gt;They also do not replace working at the bench. Some problems still require an oscilloscope, logic
analyser, or direct access to the complete device.&lt;/p&gt;
&lt;p&gt;But there is a large space between a host-side unit test and a manual hardware investigation.
On-target unit tests fill that space. They run early enough to catch hardware-dependent mistakes
before they reach system testing, while remaining small enough to run on every change.&lt;/p&gt;
&lt;p&gt;Semihosting is what makes those tests behave like normal programs from the outside. It gives
bare-metal firmware a way to return an exit status. &lt;code&gt;defmt-test&lt;/code&gt; uses that mechanism to report the
result of a Rust test suite, and &lt;code&gt;probe-rs&lt;/code&gt; turns it into the exit code that Cargo and CI already
understand.&lt;/p&gt;
&lt;p&gt;OnMCU extends that same path to remotely hosted hardware. The test still runs on a real
microcontroller, but the board no longer has to be connected to the machine that started the test.&lt;/p&gt;
&lt;p&gt;Our goal is simple: every commit should be testable on real silicon without requiring every team to
build and maintain its own hardware infrastructure.&lt;/p&gt;
</description>
      </item>
      <item>
          <title>Why do we even test?</title>
          <pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://www.onmcu.com/blog/why-do-we-even-test/</link>
          <guid>https://www.onmcu.com/blog/why-do-we-even-test/</guid>
          <description xml:base="https://www.onmcu.com/blog/why-do-we-even-test/">&lt;p&gt;Everybody, everywhere is constantly testing. Most subscription services, from newspapers to video streaming, offer free trials so that potential customers can test the service before committing to it. The goal is to convince users that the service is indeed worth the money and prove that it meets all (or at least most) of their expectations.&lt;/p&gt;
&lt;p&gt;Using such trials or samples to test products is so common that it seems almost crazy to ask this question: &lt;em&gt;Why do we even test?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While developing &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.onmcu.com&quot;&gt;OnMCU&lt;/a&gt; (my new startup for embedded testing automation), I’ve been thinking about how to approach systems engineering for embedded devices. Especially regarding testing software of embedded systems, I encountered a few more interesting questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is the goal of testing?&lt;/li&gt;
&lt;li&gt;What can testing do and what can it not do?&lt;/li&gt;
&lt;li&gt;And since in the real world things inevitably go wrong at some point: How should errors be handled?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the last one leading to an even more fundamental question: &lt;em&gt;What is an error even?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Fortunately, many smart people, like for example the famous Edsger Dijkstra, have been asking the same questions and they came up with answers. This blog post is a small collection of what I have found to be the most interesting ideas, trying to give credit wherever possible.&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-point-of-testing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-the-point-of-testing&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;What is the Point of Testing?&lt;/h2&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tests &lt;em&gt;should&lt;/em&gt; be used to prove correctness, not to find bugs&lt;/li&gt;
&lt;li&gt;Tests can only prove the presence of bugs, not their absence&lt;/li&gt;
&lt;li&gt;Program components need to be structured with testing in mind — otherwise the program is not likely to be correct&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The goal of test is to prove correctness, not to find bugs. If you study the quality movement you’ll see this precept has revolutionized manufacturing. Alas, it hasn’t seeped into software engineering.&lt;/p&gt;
&lt;p&gt;— Jack Ganssle (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.ganssle.com/tem/tem500.html&quot;&gt;https://www.ganssle.com/tem/tem500.html&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In general, I think, this is a good point to start. Write tests with the goal of proving correctness under all possible circumstances. For unit tests this usually boils down to testing a procedure against an expected set of inputs. By expected I do not necessarily mean the “wanted” set of inputs — you need to expect the unwanted and unexpected, as well. In practice, unfortunately, this is easier said than done. We are working with actual hardware and hardware itself is, to some extent, undefined behavior.&lt;/p&gt;
&lt;p&gt;A sensor may die, a connection may corrode or some ominous cosmic ray may flip your precious bit. Not all of this can be tested and “in the wild”, things (or users!) will happen you never thought of. In the end, we have to settle for statistical statements about the likelihood of things going sideways. Which brings me to a quote by the famous inventor of the Dijkstra algorithm, Edsger Dijkstra:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Recalling that our true concern is with really large programs, we observe as an aside that the size itself requires a high confidence level for the individual program components. If the chance of correctness of an individual component equals p, the chance of correctness of a whole program, composed of N such components, is something like $P = p^N$ As $N$ will be very large, $p$ should be very, very close to $1$ if we desire $P$ to differ significantly from zero!&lt;/p&gt;
&lt;p&gt;— Edsger Dijkstra (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD249/EWD249.html&quot;&gt;https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD249/EWD249.html&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Especially for embedded applications, we might want to assign weights to these probabilities. In most, if not all, applications, not every single part of the program is super critical. So it is generally a good idea to identify the most critical parts, isolate them and increase the probability of them being correct.&lt;/p&gt;
&lt;p&gt;Imagine a washing machine: one very critical part is ensuring the door cannot be opened while the machine is operational. We might want to ensure this not only in software, but even in hardware. Safety first! When the machine has finished its job, an LED might light up and short sound of success may beep out of a speaker. Not a critical feature at all — most of the time, nobody will hear it anyway. So we might only want to ensure that this part of the program, should it fail, does not block or crash the rest of the program. And call it a day.&lt;/p&gt;
&lt;p&gt;And since&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Program testing can be used to show the presence of bugs, but never to show their absence&lt;/p&gt;
&lt;p&gt;— Edsger Dijkstra (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD249/EWD249.html&quot;&gt;https://www.cs.utexas.edu/~EWD/transcriptions/EWD02xx/EWD249/EWD249.html&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;we need to have more testing wherever we need to show more bugs’ presence.&lt;/p&gt;
&lt;p&gt;What do we learn from this? Proving correctness is inherently difficult. And even though testing is a powerful tool, it is not capable of proving a program’s correctness. Still, we should write tests as if it were.&lt;/p&gt;
&lt;h2 id=&quot;what-is-an-error&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-an-error&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;What is an Error?&lt;/h2&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; Errors can be “bugs” (programming errors) or “forks in the control flow” (unexpected/unwanted inputs)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When people talk about errors, they might mean two entirely different things.&lt;/p&gt;
&lt;p&gt;According to the Cambridge Dictionary an error may be&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;something done or written by accident that is not correct, not accurate, or does not give the right result&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a mistake, esp. in a way that can be discovered as wrong&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first one is an error, as sometimes found by the compiler. It is often called a “bug”, but to be precise, it is a “programmer error”.&lt;/p&gt;
&lt;p&gt;The second one is a little more difficult, although it is the more common usage of “error” in programming. We can think of this “mistake” as a “Fork in the Control Flow” (Tim McNamara, IEEE SE Radio #644), where the program receives an input that is unexpected (or not understandable, i.e., some precondition is not satisfied) and thus deviates from the “ideal” program flow. For example, the program tries to open a file that does not exist. This might not be the intended or “good” case, but it is something that can be handled (e.g., by creating the file).&lt;/p&gt;
&lt;p&gt;In a sense, such a “mistake” can become a “bug” if it is not handled correctly. &lt;sup class=&quot;footnote-reference&quot; id=&quot;fr-1-1&quot;&gt;&lt;a href=&quot;https://www.onmcu.com/blog/why-do-we-even-test/#fn-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I think Dijkstra is right to a large extent when he writes that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We could, for instance, begin with cleaning up our language by no longer calling a bug a bug but by calling it an error. It is much more honest because it squarely puts the blame where it belongs, viz. with the programmer who made the error. The animistic metaphor of the bug that maliciously sneaked in while the programmer was not looking is intellectually dishonest as it disguises that the error is the programmer’s own creation.&lt;/p&gt;
&lt;p&gt;— Edsgar Dijkstra (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD1036.html&quot;&gt;https://www.cs.utexas.edu/~EWD/transcriptions/EWD10xx/EWD1036.html&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;because only identifying the source of an error allows us to take appropriate measures.&lt;/p&gt;
&lt;h2 id=&quot;to-panic-or-not-to-panic&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-panic-or-not-to-panic&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;To Panic or Not to Panic&lt;/h2&gt;
&lt;p&gt;Talking about appropriate measures: what does this even mean?&lt;/p&gt;
&lt;p&gt;Deciding on what to do often boils down to the question of “to panic or not to panic?”, that is, should the program shut down (somewhat gracefully) or can the error be handled in a way that allows for the program to continue (the aforementioned “fork in the control flow”).&lt;/p&gt;
&lt;p&gt;This is not an easy question to answer. When writing a small program for oneself, maybe a couple of hundred lines of code, sophisticated error handling may not be necessary. Often it is even okay to just let the program crash. In libraries or programs that are used by other people, error handling needs to be more than crashing.&lt;/p&gt;
&lt;p&gt;As a general rule, you should panic only when this is literally the only way to go. There are not many occasions where this is true. More often than not, the same error will occur on the next run of the program, so just restarting it will not help for long.&lt;/p&gt;
&lt;h2 id=&quot;your-input&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#your-input&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;Your Input!&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Who (and whose ideas) have I missed?&lt;/li&gt;
&lt;li&gt;What are your biggest pain points with testing?&lt;/li&gt;
&lt;li&gt;What has been your greatest testing success?&lt;/li&gt;
&lt;li&gt;Is there an argument to be made about testing from a Bayesian or information theoretic perspective? That would seem quite natural to me, but I haven’t come up with a good idea about that. Testing reduces uncertainty, so it reduces entropy by giving us more prior information about the system. What implications does that have?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have any remarks, comments or ideas, drop me an e-mail to
&lt;a class=&quot;js m-protected-mod&quot; href=&quot;#anVsaWFuQG9ubWN1LmNvbQ==&quot; target=&quot;_blank&quot; title=&quot;Mail&quot;&gt;&lt;i type=&quot;Button&quot; class=&quot;svg mail&quot; title=&quot;Mail&quot;&gt;&lt;/i&gt;&lt;/a&gt;
&lt;a class=&quot;js m-protected-mod&quot; href=&quot;#anVsaWFuQG9ubWN1LmNvbQ==&quot; target=&quot;_blank&quot; title=&quot;Mail&quot;&gt;&lt;span class=&quot;js m-protected-mod-innertext&quot;&gt;#anVsaWFuQG9ubWN1LmNvbQ==&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;How can we ensure that it is handled “correctly”? By testing? Unfortunately not, we can’t usually prove correctness with tests alone… &lt;a href=&quot;https://www.onmcu.com/blog/why-do-we-even-test/#fr-1-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      </item>
    </channel>
</rss>
