<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.five-embeddev.com//feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.five-embeddev.com//" rel="alternate" type="text/html" /><updated>2026-01-24T13:15:57+00:00</updated><id>https://www.five-embeddev.com//feed.xml</id><title type="html">Five EmbedDev</title><subtitle>Embedded Systems Developer RISC-V Blog</subtitle><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><entry><title type="html">Building a header-only C++20 coroutine runtime for bare-metal RISC-V</title><link href="https://www.five-embeddev.com//articles/2024/11/24/part-1-cpp20-coroutines-runtime/" rel="alternate" type="text/html" title="Building a header-only C++20 coroutine runtime for bare-metal RISC-V" /><published>2024-11-24T00:00:00+00:00</published><updated>2024-11-24T00:00:00+00:00</updated><id>https://www.five-embeddev.com//articles/2024/11/24/part-1-cpp20-coroutines-runtime</id><content type="html" xml:base="https://www.five-embeddev.com//articles/2024/11/24/part-1-cpp20-coroutines-runtime/"><![CDATA[<h2 id="creating-the-coroutines-runtime-infrastructure">Creating the coroutines runtime infrastructure</h2>

<p>A simple coroutine example was presented in <a href="/articles/2024/11/24/part-2-cpp20-coroutines-short/">“C++20 coroutines, header only, without an OS”</a>. This post describes the runtime used for that example in detail.</p>

<p>This story is also published on <a href="https://philmulholland.medium.com/c-20-coroutines-a-header-only-runtime-for-re-entrant-tasks-87cb8e2c0ee1">Medium</a>.</p>

<h3 id="summary-of-the-runtime-files">Summary of the runtime files</h3>

<p>The runtime for this example is a set of include files in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro"><code class="language-plaintext highlighter-rouge">include/coro</code></a>. These files are used:</p>

<ul>
  <li><a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/nop_task.hpp"><code class="language-plaintext highlighter-rouge">nop_task.hpp</code></a> : Task structure including <code class="language-plaintext highlighter-rouge">promise_type</code> to conform the C++ coroutines task concept.</li>
  <li><a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/scheduler.hpp"><code class="language-plaintext highlighter-rouge">scheduler.hpp</code></a> : Generic scheduler class that can manage a set of <code class="language-plaintext highlighter-rouge">std::coroutine_handle</code> to determine when they should resume and implement the resumption.</li>
  <li><a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/awaitable_timer.hpp"><code class="language-plaintext highlighter-rouge">awaitable_timer.hpp</code></a> : An “awaitable” class that can be used with <code class="language-plaintext highlighter-rouge">co_await</code> to schedule a coroutines to wake up after a given <code class="language-plaintext highlighter-rouge">std::chono</code> delay.</li>
  <li><a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/static_list.hpp"><code class="language-plaintext highlighter-rouge">static_list.hpp</code></a>: An alternative to <code class="language-plaintext highlighter-rouge">std::list</code> that uses custom memory allocation from a static region to avoid heap usage.</li>
  <li><a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/awaitable_priority.hpp"><code class="language-plaintext highlighter-rouge">awaitable_priority.hpp</code></a>: An alternative “awaitable” class for tasks to be scheduled to wake according to priority.</li>
</ul>

<p><strong>NOTE:</strong> All classes here are designed to not use the heap for allocation. They will allocate all memory from statically declared buffers.</p>

<!--more-->

<h3 id="the-coroutine-task-concept">The coroutine task concept</h3>

<p>The <code class="language-plaintext highlighter-rouge">nop_task</code> class in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/nop_task.hpp"><code class="language-plaintext highlighter-rouge">nop_task.hpp</code></a> file implements the coroutine task concept.</p>

<p>A coroutine task includes a promise concept with no return values. The important structures in this file are<code class="language-plaintext highlighter-rouge">struct nop_task</code> / <code class="language-plaintext highlighter-rouge">struct nop_task::promise_type</code>. This is implemented as described in <a href="https://en.cppreference.com/w/cpp/language/coroutines">CPP Reference</a>.</p>

<p>This task structure will be allocated each time a coroutine is called. To avoid heap allocation static memory allocation is used (to be described below). When using a memory constrained platform it is important to understand that the number of coroutines that can be called is restricted by the memory allocated for <code class="language-plaintext highlighter-rouge">nop_task::task_heap_</code>.</p>

<p>The relationships between the task classes is shown in the following class diagram:</p>

<p><img src="/images/cpp-coro/nop_task.svg" alt="Task" /></p>

<h3 id="the-awaitable-concept">The awaitable concept</h3>

<p>The classes in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/awaitable_timer.hpp"><code class="language-plaintext highlighter-rouge">awaitable_timer.hpp</code></a> and <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/awaitable_priority.hpp"><code class="language-plaintext highlighter-rouge">awaitable_priority.hpp</code></a> represent asynchronous events that pause the coroutine task until an event occurs.</p>

<p>These classes are designed to be returned from a <code class="language-plaintext highlighter-rouge">co_await</code>, this ensures a task can be scheduled to be resumed on a later event.</p>

<p>The <code class="language-plaintext highlighter-rouge">awaitable_timer</code> class implements the awaitable concept described in <a href="https://en.cppreference.com/w/cpp/language/coroutines">CPP Reference</a>, and also the <code class="language-plaintext highlighter-rouge">co_await</code> operator that is overloaded to take the <code class="language-plaintext highlighter-rouge">scheduler_delay</code> struct and return <code class="language-plaintext highlighter-rouge">awaitable_timer</code>. An additional concept of the <code class="language-plaintext highlighter-rouge">scheduler</code> class is being used to manage the coroutine handle and wake up conditions that are used to implement coroutine task pause.</p>

<p>The relationships between the awaitable classes is shown in the following class diagram:</p>

<p><img src="/images/cpp-coro/awaitable.svg" alt="Awaitable" /></p>

<h3 id="the-scheduler-class">The scheduler class</h3>

<p>The classes in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/include/coro/scheduler.hpp"><code class="language-plaintext highlighter-rouge">scheduler.hpp</code></a> are designed to do the work of managing the coroutines that are paused. It is a template class, parameterized according to the type of event that should be scheduled, and the maximum number of concurrent active tasks.</p>

<p>The scheduler does the work that would be done by an RTOS or General Purpose OS. It manages a task list of waiting tasks with wake conditions and resumes them on the wake event.</p>

<p>The awaitable classes, introduced above, will insert paused tasks via <code class="language-plaintext highlighter-rouge">insert()</code>. The active execution context must call <code class="language-plaintext highlighter-rouge">resume()</code> to resume the paused tasks. Each entry in the task list is a <code class="language-plaintext highlighter-rouge">schedule_entry</code> structure. The classe are templates specialized by the wake up condition.</p>

<p>This scheduler class is not a concept required by C++ coroutines, but in this example it is needed as there is no operating system scheduler.</p>

<p>The relationships between scheduler classes is shown in the following class diagram:</p>

<p><img src="/images/cpp-coro/scheduler.svg" alt="Software Timer" /></p>

<h3 id="using-the-awaitable-and-scheduler-classes-to-create-a-software-timer">Using the awaitable and scheduler classes to create a software timer</h3>

<p>The awaitable class and scheduler are combined to implement the software timer feature. The following diagram shows how the classes relate.</p>

<p><img src="/images/cpp-coro/software_timer.svg" alt="Software Timer" /></p>

<h3 id="walk-through-of-the-detailed-sequence-of-suspend-and-resume">Walk through of the detailed sequence of suspend and resume</h3>

<p>Now the concrete classes have been defined, the sequence to suspend and resume a coroutine class can be show.</p>

<p>It is shown below in 3 stages in relation to the simple timer example.</p>

<h4 id="1-setup-a-coroutine-and-suspend-on-the-first-wait">1. Setup a coroutine, and suspend on the first wait.</h4>

<p><img src="/images/cpp-coro/task_sequence.svg" alt="Task Sequence" /></p>

<h4 id="2-resume-and-suspend-iterate-over-several-time-delays">2. Resume and suspend, iterate over several time delays.</h4>

<p><img src="/images/cpp-coro/task_sequence_001.svg" alt="Task Sequence" /></p>

<h4 id="3-complete-iterating-and-exit-coroutines">3. Complete iterating and exit coroutines.</h4>

<p><img src="/images/cpp-coro/task_sequence_002.svg" alt="Task Sequence" /></p>

<h2 id="testing">Testing</h2>

<p>The runtime has some basic unit testing implemented in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/test">test</a> using the <a href="https://www.throwtheswitch.org/unity">unity</a> test framework.  The tests are not comprehensive, but run independent of hardware as the runtime is host &amp; OS independent.  The tests are compiled for the host OS and run locally.</p>

<h2 id="summary">Summary</h2>

<p>The runtime presented in this article is not meant for production usage and has the bare minimal functionality to implement a re-entrant function using a software timer.</p>

<p>However, it does show the potential of C++ coroutines to be applied to real time applications that are portable across different OS and target architectures.</p>

<h2 id="appendix">Appendix</h2>

<h3 id="references">References</h3>

<p>I won’t explain the details of C++ coroutines, there are much better resources. I used the following to understand coroutines:</p>

<ul>
  <li><a href="https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer">Lewis Baker’s “C++ coroutines: Understanding Symmetric-Transfer” -</a></li>
  <li><a href="https://godbolt.org/z/-Kw6Nf">Lewis Baker’s “C++ coroutines: Understanding Symmetric-Transfer” - the code</a></li>
  <li><a href="https://github.com/lewissbaker/cppcoro">CPPcoro library, has not been updated for gcc support - however the documentation is great!</a></li>
  <li><a href="">The folly library has an updated coro from Lewis Baker</a>, and <a href="https://blog.the-pans.com/build-folly-coro/">some info on enabling coroutines </a></li>
  <li><a href="https://devblogs.microsoft.com/oldnewthing/20210504-01/?p=105178">Raymond Chen’s explanations, and demonstration of legacy callback integration</a></li>
  <li><a href="https://mariusbancila.ro/blog/2020/06/22/a-cpp20-coroutine-example/">Some nice examples here</a></li>
  <li><a href="https://en.cppreference.com/w/cpp/language/coroutines">CPP Reference is incomplete, but a start</a></li>
</ul>

<h3 id="a-few-implementation-details">A Few implementation details</h3>

<ul>
  <li>Tasks are created at the first call to an asynchronous routine. The allocated task data structure is minimal.</li>
  <li>Tasks do not have a dedicated stack, they will be restored to the stack where they are woken.</li>
  <li>Context switching stack management is implemented by the compiler, no custom assembler routines are needed (such as <a href="https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/4d4f8d0d50bedc37e1d07c96aded7d2bc20f0d6c/portable/GCC/RISC-V/portASM.S">portASM.s in FreeRTOS</a>). This is possible as there is no pre-emptive context switching.</li>
  <li>The scheduler can be made a C++ object, and context switching can be controlled programmatically..</li>
</ul>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="articles" /><category term="C++" /><category term="baremetal" /><category term="coroutines" /><summary type="html"><![CDATA[Creating the coroutines runtime infrastructure A simple coroutine example was presented in “C++20 coroutines, header only, without an OS”. This post describes the runtime used for that example in detail. This story is also published on Medium. Summary of the runtime files The runtime for this example is a set of include files in include/coro. These files are used: nop_task.hpp : Task structure including promise_type to conform the C++ coroutines task concept. scheduler.hpp : Generic scheduler class that can manage a set of std::coroutine_handle to determine when they should resume and implement the resumption. awaitable_timer.hpp : An “awaitable” class that can be used with co_await to schedule a coroutines to wake up after a given std::chono delay. static_list.hpp: An alternative to std::list that uses custom memory allocation from a static region to avoid heap usage. awaitable_priority.hpp: An alternative “awaitable” class for tasks to be scheduled to wake according to priority. NOTE: All classes here are designed to not use the heap for allocation. They will allocate all memory from statically declared buffers. The coroutine task concept The nop_task class in nop_task.hpp file implements the coroutine task concept. A coroutine task includes a promise concept with no return values. The important structures in this file arestruct nop_task / struct nop_task::promise_type. This is implemented as described in CPP Reference. This task structure will be allocated each time a coroutine is called. To avoid heap allocation static memory allocation is used (to be described below). When using a memory constrained platform it is important to understand that the number of coroutines that can be called is restricted by the memory allocated for nop_task::task_heap_. The relationships between the task classes is shown in the following class diagram: The awaitable concept The classes in awaitable_timer.hpp and awaitable_priority.hpp represent asynchronous events that pause the coroutine task until an event occurs. These classes are designed to be returned from a co_await, this ensures a task can be scheduled to be resumed on a later event. The awaitable_timer class implements the awaitable concept described in CPP Reference, and also the co_await operator that is overloaded to take the scheduler_delay struct and return awaitable_timer. An additional concept of the scheduler class is being used to manage the coroutine handle and wake up conditions that are used to implement coroutine task pause. The relationships between the awaitable classes is shown in the following class diagram: The scheduler class The classes in scheduler.hpp are designed to do the work of managing the coroutines that are paused. It is a template class, parameterized according to the type of event that should be scheduled, and the maximum number of concurrent active tasks. The scheduler does the work that would be done by an RTOS or General Purpose OS. It manages a task list of waiting tasks with wake conditions and resumes them on the wake event. The awaitable classes, introduced above, will insert paused tasks via insert(). The active execution context must call resume() to resume the paused tasks. Each entry in the task list is a schedule_entry structure. The classe are templates specialized by the wake up condition. This scheduler class is not a concept required by C++ coroutines, but in this example it is needed as there is no operating system scheduler. The relationships between scheduler classes is shown in the following class diagram: Using the awaitable and scheduler classes to create a software timer The awaitable class and scheduler are combined to implement the software timer feature. The following diagram shows how the classes relate. Walk through of the detailed sequence of suspend and resume Now the concrete classes have been defined, the sequence to suspend and resume a coroutine class can be show. It is shown below in 3 stages in relation to the simple timer example. 1. Setup a coroutine, and suspend on the first wait. 2. Resume and suspend, iterate over several time delays. 3. Complete iterating and exit coroutines. Testing The runtime has some basic unit testing implemented in test using the unity test framework. The tests are not comprehensive, but run independent of hardware as the runtime is host &amp; OS independent. The tests are compiled for the host OS and run locally. Summary The runtime presented in this article is not meant for production usage and has the bare minimal functionality to implement a re-entrant function using a software timer. However, it does show the potential of C++ coroutines to be applied to real time applications that are portable across different OS and target architectures. Appendix References I won’t explain the details of C++ coroutines, there are much better resources. I used the following to understand coroutines: Lewis Baker’s “C++ coroutines: Understanding Symmetric-Transfer” - Lewis Baker’s “C++ coroutines: Understanding Symmetric-Transfer” - the code CPPcoro library, has not been updated for gcc support - however the documentation is great! The folly library has an updated coro from Lewis Baker, and some info on enabling coroutines Raymond Chen’s explanations, and demonstration of legacy callback integration Some nice examples here CPP Reference is incomplete, but a start A Few implementation details Tasks are created at the first call to an asynchronous routine. The allocated task data structure is minimal. Tasks do not have a dedicated stack, they will be restored to the stack where they are woken. Context switching stack management is implemented by the compiler, no custom assembler routines are needed (such as portASM.s in FreeRTOS). This is possible as there is no pre-emptive context switching. The scheduler can be made a C++ object, and context switching can be controlled programmatically..]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Exploring C++20 coroutines for embedded and bare-metal development on RISC-V platforms</title><link href="https://www.five-embeddev.com//articles/2024/11/24/part-2-cpp20-coroutines-short/" rel="alternate" type="text/html" title="Exploring C++20 coroutines for embedded and bare-metal development on RISC-V platforms" /><published>2024-11-24T00:00:00+00:00</published><updated>2024-11-24T00:00:00+00:00</updated><id>https://www.five-embeddev.com//articles/2024/11/24/part-2-cpp20-coroutines-short</id><content type="html" xml:base="https://www.five-embeddev.com//articles/2024/11/24/part-2-cpp20-coroutines-short/"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>This post is about using C++ coroutines to suspend and resume functions in real time. The objective is a simple way of building real time tasks using only C++, without the need for an RTOS or operating system kernel.</p>

<p>Coroutines are functions that can be suspended and resumed, using the keywords <a href="https://en.cppreference.com/w/cpp/language/coroutines"><code class="language-plaintext highlighter-rouge">co_await</code>, <code class="language-plaintext highlighter-rouge">co_yield</code> and
<code class="language-plaintext highlighter-rouge">co_return</code></a>. The C++20 standard introduced coroutines to the language.</p>

<p>C++ standardized the keywords and type concepts for coroutines, but it did not standardize a
 runtime<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>. The lack of a standard runtime has made them hard to use them “out of the box”, but the implementation of coroutines is very adaptable to different use cases.</p>

<p>Here I use a simple runtime implementing C++20
coroutines on bare metal (no operating system) for RISC-V, using the
<code class="language-plaintext highlighter-rouge">co_await</code> keyword. This is done by passing the real time scheduler and resume time condition as the argument to the asynchronous wait operator.</p>

<p>The runtime is described in detail in <a href="/articles/2024/11/24/part-1-cpp20-coroutines-runtime/">this post</a>.</p>

<p>This story is also published on <a href="https://philmulholland.medium.com/c-20-coroutines-re-entrant-scheduled-tasks-no-os-required-061c20efafad">Medium</a>.</p>

<!--more-->

<h2 id="why-coroutines">Why coroutines?</h2>

<p>I’m interested in coroutines for the follow benefits:</p>

<ul>
  <li>Event driven asynchronous functions can be written using a control/data flow in a single body of code that can be easy to understand.</li>
  <li>Code is portable so that the same code can be tested on development OS and target systems.</li>
  <li>Resource efficiency, in terms of memory usage (stack and heap) and CPU cycle usage.</li>
</ul>

<h2 id="a-software-timer-example">A software timer example</h2>

<p>This article will build a simple software timer example. The function has a loop that pauses for several micro seconds before iterating again. While the loop is paused the control flow returns to the caller function.</p>

<h3 id="a-simple-coroutines-task">A simple coroutines task</h3>

<p>A simple task <code class="language-plaintext highlighter-rouge">periodic</code> is defined in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/src/example_simple.cpp#L30">example_simple.cpp</a>.
It takes <code class="language-plaintext highlighter-rouge">scheduler</code>, <code class="language-plaintext highlighter-rouge">period</code> and <code class="language-plaintext highlighter-rouge">resume_count</code> as arguments and asynchronously waits <code class="language-plaintext highlighter-rouge">period</code> microseconds for 10 iterations, updating the <code class="language-plaintext highlighter-rouge">resume_count</code> value each iteration.</p>

<p>The <code class="language-plaintext highlighter-rouge">scheduler</code> passed as an argument is not strictly necessary for C++ coroutines, but is used to make the ownership of the context of each task explicit. (It could be possible to use a global scheduler, such as when implementing via OS threads.)</p>

<p>The task returns <code class="language-plaintext highlighter-rouge">nop_task</code>. This is a special structure that is linked to the coroutines implementation. In this case a “nop task” refers to a task that does not return a value via <code class="language-plaintext highlighter-rouge">co_return</code>.</p>

<figure class="highlight"><pre><code class="language-c--" data-lang="c++"><span class="k">template</span><span class="o">&lt;</span><span class="k">typename</span> <span class="nc">SCHEDULER</span><span class="p">&gt;</span>
<span class="n">nop_task</span> <span class="nf">periodic</span><span class="p">(</span>
    <span class="n">SCHEDULER</span><span class="o">&amp;</span> <span class="n">scheduler</span><span class="p">,</span>
    <span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">microseconds</span> <span class="n">period</span><span class="p">,</span>
    <span class="k">volatile</span> <span class="kt">uint32_t</span><span class="o">&amp;</span> <span class="n">resume_count</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">driver</span><span class="o">::</span><span class="n">timer</span><span class="o">&lt;&gt;</span> <span class="n">mtimer</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">co_await</span> <span class="n">scheduled_delay</span><span class="p">{</span> <span class="n">scheduler</span><span class="p">,</span> <span class="n">period</span> <span class="p">};</span>
        <span class="o">*</span><span class="n">timestamp_resume</span><span class="p">[</span><span class="n">resume_count</span><span class="p">]</span> <span class="o">=</span> <span class="n">mtimer</span><span class="p">.</span><span class="n">get_time</span><span class="o">&lt;</span><span class="n">driver</span><span class="o">::</span><span class="n">timer</span><span class="o">&lt;&gt;::</span><span class="n">timer_ticks</span><span class="o">&gt;</span><span class="p">().</span><span class="n">count</span><span class="p">();</span>
        <span class="n">resume_count</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">co_return</span><span class="p">;</span> <span class="c1">// Not strictly needed</span>
<span class="p">}</span></code></pre></figure>

<p>The function has the following behavior:</p>

<ul>
  <li>Take <code class="language-plaintext highlighter-rouge">period</code> as a parameter in microseconds.</li>
  <li>Keep track of the number of iteraton via the <code class="language-plaintext highlighter-rouge">resume_count</code> counter.</li>
  <li>Iterate 10 times.</li>
  <li>For each iteration,
    <ul>
      <li>wait <code class="language-plaintext highlighter-rouge">period</code> using the <code class="language-plaintext highlighter-rouge">co_await</code> keyword.</li>
      <li>then increment <code class="language-plaintext highlighter-rouge">resume_count</code>.</li>
    </ul>
  </li>
  <li>Use <code class="language-plaintext highlighter-rouge">co_return</code> to exit the coroutines.</li>
</ul>

<p>The following sequence diagram shows an abstract coroutine execution where an abstracted OS exists to handle the scheduling of process execution. (<a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/docs/diagrams/tast_sequence_abstract.puml">PlantUML source</a>)</p>

<p><img src="/images/cpp-coro/tast_sequence_abstract.svg" alt="Task Sequence" /></p>

<h3 id="calling-the-simple-coroutine-task">Calling the simple coroutine task</h3>

<p>The <code class="language-plaintext highlighter-rouge">example_simple()</code> function in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/src/example_simple.cpp#L41">example_simple.cpp</a> calls the <code class="language-plaintext highlighter-rouge">periodic</code> function once, with <code class="language-plaintext highlighter-rouge">100ms</code> as the <code class="language-plaintext highlighter-rouge">period</code> value.</p>

<p>The <code class="language-plaintext highlighter-rouge">scheduler_delay&lt;mtimer_clock&gt;</code> is a scheduler class that will manage the software timer to wake each coroutine at the appropriate time, using our RISC-V machine mode timer driver <code class="language-plaintext highlighter-rouge">mtimer</code>.</p>

<figure class="highlight"><pre><code class="language-c--" data-lang="c++">    <span class="n">driver</span><span class="o">::</span><span class="n">timer</span><span class="o">&lt;&gt;</span> <span class="n">mtimer</span><span class="p">;</span>
    <span class="c1">// Class to manage timer coroutines</span>
    <span class="n">scheduler_delay</span><span class="o">&lt;</span><span class="n">mtimer_clock</span><span class="o">&gt;</span> <span class="n">scheduler</span><span class="p">;</span>
    <span class="c1">// Run two concurrent loops. The first loop will run concurrently to the second loop.</span>
    <span class="k">auto</span> <span class="n">t0</span> <span class="o">=</span> <span class="n">periodic</span><span class="p">(</span><span class="n">scheduler</span><span class="p">,</span> <span class="mx">100ms</span><span class="p">,</span> <span class="n">resume_simple</span><span class="p">);</span></code></pre></figure>

<h3 id="resuming-the-coroutine-tasks">Resuming the coroutine tasks</h3>

<p>For this example the scheduler is an object instantiated in the <code class="language-plaintext highlighter-rouge">example_simple()</code> function. It needs to be called explicitly to calculate when each coroutine needs to be woken and resumed. This is a convention of the runtime for this example, and not a required convention for C++ coroutines.</p>

<p>The tasks are resumed in the <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/src/example_simple.cpp#L82">WFI busy loop of <code class="language-plaintext highlighter-rouge">example_Simple()</code></a> when <code class="language-plaintext highlighter-rouge">scheduler.update()</code> is called. However, as the scheduler is just a C++ class, this can be called from other locations, such as a timer interrupt handler.</p>

<figure class="highlight"><pre><code class="language-c--" data-lang="c++">    <span class="k">do</span> <span class="p">{</span>
        <span class="c1">// Get a delay to the next coroutines wake up</span>
        <span class="n">schedule_by_delay</span><span class="o">&lt;</span><span class="n">mtimer_clock</span><span class="o">&gt;</span> <span class="n">now</span><span class="p">;</span>
        <span class="k">auto</span> <span class="p">[</span><span class="n">pending</span><span class="p">,</span> <span class="n">next_wake</span><span class="p">]</span> <span class="o">=</span> <span class="n">scheduler</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">now</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">pending</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// Next wakeup</span>
            <span class="n">mtimer</span><span class="p">.</span><span class="n">set_time_cmp</span><span class="p">(</span><span class="n">next_wake</span><span class="o">-&gt;</span><span class="n">delay</span><span class="p">());</span>
            <span class="c1">// Timer interrupt enable</span>
            <span class="n">riscv</span><span class="o">::</span><span class="n">csrs</span><span class="p">.</span><span class="n">mstatus</span><span class="p">.</span><span class="n">mie</span><span class="p">.</span><span class="n">clr</span><span class="p">();</span>
            <span class="n">riscv</span><span class="o">::</span><span class="n">csrs</span><span class="p">.</span><span class="n">mie</span><span class="p">.</span><span class="n">mti</span><span class="p">.</span><span class="n">set</span><span class="p">();</span>
            <span class="c1">// WFI Should be called while interrupts are disabled </span>
            <span class="c1">// to ensure interrupt enable and WFI is atomic.            </span>
            <span class="n">core</span><span class="p">.</span><span class="n">wfi</span><span class="p">();</span>
        <span class="p">]</span>
    <span class="p">}</span> <span class="k">while</span><span class="p">(</span><span class="nb">true</span><span class="p">)</span></code></pre></figure>

<p>For example as the IRQ handler in this example is a lambda function, we could also capture the scheduler and run the timer coroutine in the IRQ handler.</p>

<figure class="highlight"><pre><code class="language-c--" data-lang="c++">    <span class="k">static</span> <span class="k">const</span> <span class="k">auto</span> <span class="n">handler</span> <span class="o">=</span> <span class="p">[</span><span class="o">&amp;</span><span class="p">](</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
        <span class="p">...</span>
        <span class="n">schedule_by_delay</span><span class="o">&lt;</span><span class="n">mtimer_clock</span><span class="o">&gt;</span> <span class="n">now</span><span class="p">;</span>
        <span class="k">auto</span> <span class="p">[</span><span class="n">pending</span><span class="p">,</span> <span class="n">next_wake</span><span class="p">]</span> <span class="o">=</span> <span class="n">scheduler</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">now</span><span class="p">);</span>
    <span class="p">};</span></code></pre></figure>

<h2 id="building-and-running-with-platform-io">Building and running with Platform IO</h2>

<p>The example can be built and run using Platform IO. The default RISC-V platforms use an old version of GCC that does not support C++20, so a custom virtual platform configured to use <a href="https://xpack.github.io/dev-tools/riscv-none-elf-gcc/">xPack 12.2.0-3 riscv-none-elf-gcc</a> and run on QEMU has been created in <a href="https://github.com/five-embeddev/baremetal-cxx-coro/tree/main/platformio/platforms/virt_riscv">platformio/platforms/virt_riscv</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>build_flags = 
    -std=c++20
    -O2
    -g
    -Wall 
    -ffunction-sections 
    -fcoroutines
    -fno-exceptions 
    -fno-rtti 
    -fno-nonansi-builtins 
    -fno-use-cxa-atexit 
    -fno-threadsafe-statics
    -nostartfiles 
    -Wl,-Map,c-hardware-access-riscv.map
</code></pre></div></div>

<p>The debug sequence shows entering the function <code class="language-plaintext highlighter-rouge">example_simple()</code>, initializing <code class="language-plaintext highlighter-rouge">scheduler_delay&lt;mtimer_clock&gt; scheduler;</code> then calling <code class="language-plaintext highlighter-rouge">periodic(scheduler, 100ms, resume_simple);</code>.</p>

<p>Once the statement <code class="language-plaintext highlighter-rouge">co_await scheduled_delay{ scheduler, period };</code> is reached the context returns to <code class="language-plaintext highlighter-rouge">example_simple()</code>. Then when <code class="language-plaintext highlighter-rouge">auto [pending, next_wake] = scheduler.resume(now);</code> is called it returns to the <code class="language-plaintext highlighter-rouge">for</code> loop in <code class="language-plaintext highlighter-rouge">periodic()</code>.</p>

<p>The coroutine handle is stored in the scheduler class by the first call to <code class="language-plaintext highlighter-rouge">co_await</code>. The  following call to <code class="language-plaintext highlighter-rouge">scheduler.resume()</code> looks up the pending coroutine handle and calls resume on the handle.</p>

<p><img src="/images/cpp-coro/coro-debug.gif" alt="Debug Sequence" /></p>

<p>The stack of the coroutine <code class="language-plaintext highlighter-rouge">periodic()</code> before resume can be seen below. It’s called from <code class="language-plaintext highlighter-rouge">example_simple()</code>.</p>

<p><img src="/images/cpp-coro/coro-debug-coro-stack-0.png" alt="Debug Stack - coro" /></p>

<p>The stack of the coroutines <code class="language-plaintext highlighter-rouge">periodic()</code> after resume can be seen below. It’s called from <code class="language-plaintext highlighter-rouge">coroutine_handle::resume</code>, which is called from <code class="language-plaintext highlighter-rouge">scheduler_ordered::resume</code>.
<img src="/images/cpp-coro/coro-debug-coro-stack-1.png" alt="Debug Stack - coro" /></p>

<p>The stack of <code class="language-plaintext highlighter-rouge">example_simple()</code> function calling <code class="language-plaintext highlighter-rouge">resume()</code> is also on the same stack. 
<img src="/images/cpp-coro/coro-debug-main-stack.png" alt="Debug Stack - main" /></p>

<h2 id="building-with-cmake-and-running-with-spike">Building with CMake and running with Spike</h2>

<p>The <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/Makefile">Makefile</a> has targets to build with <a href="https://www.five-embeddev.com/baremetal/cmake/">CMake</a>.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>make target
cmake <span class="se">\</span>
        <span class="nt">-DCMAKE_TOOLCHAIN_FILE</span><span class="o">=</span>cmake/riscv.cmake <span class="se">\</span>
        <span class="nt">-DCMAKE_EXPORT_COMPILE_COMMANDS</span><span class="o">=</span>ON <span class="se">\</span>
        <span class="nt">-B</span> build_target <span class="se">\</span>
        <span class="nt">-S</span> <span class="nb">.</span>
cmake <span class="nt">--build</span> build_target <span class="nt">--verbose</span>
 </code></pre></figure>

<p>The <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/Makefile">Makefile</a> also has targets to simulate and trace with the standard RISC-V ISA simulator, spike.  The <a href="https://www.five-embeddev.com/toolchain/2022/09/02/spike-fork/">forked spike with VCD tracing</a> is used. The forked spike is included in a <a href="https://www.five-embeddev.com/toolchain/2024/06/15/docker/">docker container</a>.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>make spike_sim
docker run <span class="se">\</span>
        <span class="nt">-it</span> <span class="se">\</span>
        <span class="nt">--rm</span> <span class="se">\</span>
        <span class="nt">-v</span> .:/project <span class="se">\</span>
        fiveembeddev/forked_riscv_spike_dev_env:latest  <span class="se">\</span>
        /opt/riscv-isa-sim/bin/spike <span class="se">\</span>
        <span class="nt">--log</span><span class="o">=</span>spike_sim.log <span class="se">\</span>
        <span class="nt">--isa</span><span class="o">=</span>rv32imac_zicsr <span class="se">\</span>
        <span class="nt">-m0x8000000</span>:0x2000,0x80000000:0x4000,0x20010000:0x6a120 <span class="se">\</span>
        <span class="nt">--priv</span><span class="o">=</span>m <span class="se">\</span>
        <span class="nt">--pc</span><span class="o">=</span>0x20010000 <span class="se">\</span>
        <span class="nt">--vcd-log</span><span class="o">=</span>spike_sim.vcd <span class="se">\</span>
        <span class="nt">--max-cycles</span><span class="o">=</span>10000000  <span class="se">\</span>
        <span class="nt">--trace-var</span><span class="o">=</span>timestamp_simple <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_0 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_1 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_2 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_3 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_4 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_5 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_6 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_7 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_8 <span class="nt">--trace-var</span><span class="o">=</span>timestamp_resume_9 <span class="nt">--trace-var</span><span class="o">=</span>resume_simple <span class="se">\</span>
        build_target/src/main.elf
docker run <span class="se">\</span>
     <span class="nt">--rm</span> <span class="se">\</span>
    <span class="nt">-v</span> .:/project <span class="se">\</span>
     fiveembeddev/riscv_gtkwave_base:latest <span class="se">\</span>
     vcd2fst spike_sim.vcd spike_sim.fst</code></pre></figure>

<p>The results can be viewed with GTKWave. The <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/spike_sim.gtkw">GTKWave savefile</a> includes <a href="https://www.five-embeddev.com/code/2024/04/21/gtkwave/">address decode and opcode decode</a> by using <a href="https://github.com/five-embeddev/build-and-verify/tree/main/docker/riscv-gtkwave">docker images</a> containing the decoders.</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">gtkwave spike_sim.fst  spike_sim.gtkw</code></pre></figure>

<p>The benefit of tracing results from the ISA is that it is easy to confirm the periodic timing of the coroutine. (For this example the parameter to <code class="language-plaintext highlighter-rouge">periodic()</code> was changed to <code class="language-plaintext highlighter-rouge">1ms</code>).</p>

<p>The periodic write to <code class="language-plaintext highlighter-rouge">resume_count</code> and <code class="language-plaintext highlighter-rouge">timestamp_resume</code> is traced to VCD so the exact timing of the coroutine execution is visible.</p>

<p><img src="/images/cpp-coro/coro-debug-gtkwave.png" alt="Debug GTKWave Trace" /></p>

<p>Using GTKWave the context switch can also be examined in detail. In the fake 1GhZ clock used by spike, the context switch takes 104ns.</p>

<p><img src="/images/cpp-coro/coro-debug-gtkwave-detail.png" alt="Debug GTKWave Trace Detail" /></p>

<h2 id="coroutine-runtime">Coroutine runtime</h2>

<p>The runtime is described in detail in <a href="/articles/2024/11/24/part-1-cpp20-coroutines-runtime/">this post</a>. The runtime for this example is in the header <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/include/embeddev_coro.hpp">embeddev_coro.hpp</a>, and it uses the <a href="https://github.com/five-embeddev/baremetal-cxx-coro/blob/main/include/embeddev_riscv.hpp">embeddev_riscv.hpp</a> header to provide a simple HAL for RISC-V and host emulation.</p>

<h2 id="summary">Summary</h2>

<p>This post describes a simple working example of how to use C++ coroutines in an embedded context. The example and context are not meant to be a realistic use case, but the simplest possible use case that involves and interrupt handler and a context switch.</p>

<p>However, the example can be built on to explore portable and lightweight asynchronous programming techniques. Future posts will look at that topic.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>The C++23 standard library provides a limited runtime for coroutines generators. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="articles" /><category term="C++" /><category term="baremetal" /><category term="coroutines" /><summary type="html"><![CDATA[Introduction This post is about using C++ coroutines to suspend and resume functions in real time. The objective is a simple way of building real time tasks using only C++, without the need for an RTOS or operating system kernel. Coroutines are functions that can be suspended and resumed, using the keywords co_await, co_yield and co_return. The C++20 standard introduced coroutines to the language. C++ standardized the keywords and type concepts for coroutines, but it did not standardize a runtime1. The lack of a standard runtime has made them hard to use them “out of the box”, but the implementation of coroutines is very adaptable to different use cases. Here I use a simple runtime implementing C++20 coroutines on bare metal (no operating system) for RISC-V, using the co_await keyword. This is done by passing the real time scheduler and resume time condition as the argument to the asynchronous wait operator. The runtime is described in detail in this post. This story is also published on Medium. Why coroutines? I’m interested in coroutines for the follow benefits: Event driven asynchronous functions can be written using a control/data flow in a single body of code that can be easy to understand. Code is portable so that the same code can be tested on development OS and target systems. Resource efficiency, in terms of memory usage (stack and heap) and CPU cycle usage. A software timer example This article will build a simple software timer example. The function has a loop that pauses for several micro seconds before iterating again. While the loop is paused the control flow returns to the caller function. A simple coroutines task A simple task periodic is defined in example_simple.cpp. It takes scheduler, period and resume_count as arguments and asynchronously waits period microseconds for 10 iterations, updating the resume_count value each iteration. The scheduler passed as an argument is not strictly necessary for C++ coroutines, but is used to make the ownership of the context of each task explicit. (It could be possible to use a global scheduler, such as when implementing via OS threads.) The task returns nop_task. This is a special structure that is linked to the coroutines implementation. In this case a “nop task” refers to a task that does not return a value via co_return. template&lt;typename SCHEDULER&gt; nop_task periodic( SCHEDULER&amp; scheduler, std::chrono::microseconds period, volatile uint32_t&amp; resume_count) { driver::timer&lt;&gt; mtimer; for (auto i = 0; i &lt; 10; i++) { co_await scheduled_delay{ scheduler, period }; *timestamp_resume[resume_count] = mtimer.get_time&lt;driver::timer&lt;&gt;::timer_ticks&gt;().count(); resume_count = i + 1; } co_return; // Not strictly needed } The function has the following behavior: Take period as a parameter in microseconds. Keep track of the number of iteraton via the resume_count counter. Iterate 10 times. For each iteration, wait period using the co_await keyword. then increment resume_count. Use co_return to exit the coroutines. The following sequence diagram shows an abstract coroutine execution where an abstracted OS exists to handle the scheduling of process execution. (PlantUML source) Calling the simple coroutine task The example_simple() function in example_simple.cpp calls the periodic function once, with 100ms as the period value. The scheduler_delay&lt;mtimer_clock&gt; is a scheduler class that will manage the software timer to wake each coroutine at the appropriate time, using our RISC-V machine mode timer driver mtimer. driver::timer&lt;&gt; mtimer; // Class to manage timer coroutines scheduler_delay&lt;mtimer_clock&gt; scheduler; // Run two concurrent loops. The first loop will run concurrently to the second loop. auto t0 = periodic(scheduler, 100ms, resume_simple); Resuming the coroutine tasks For this example the scheduler is an object instantiated in the example_simple() function. It needs to be called explicitly to calculate when each coroutine needs to be woken and resumed. This is a convention of the runtime for this example, and not a required convention for C++ coroutines. The tasks are resumed in the WFI busy loop of example_Simple() when scheduler.update() is called. However, as the scheduler is just a C++ class, this can be called from other locations, such as a timer interrupt handler. do { // Get a delay to the next coroutines wake up schedule_by_delay&lt;mtimer_clock&gt; now; auto [pending, next_wake] = scheduler.update(now); if (pending) { // Next wakeup mtimer.set_time_cmp(next_wake-&gt;delay()); // Timer interrupt enable riscv::csrs.mstatus.mie.clr(); riscv::csrs.mie.mti.set(); // WFI Should be called while interrupts are disabled // to ensure interrupt enable and WFI is atomic. core.wfi(); ] } while(true) For example as the IRQ handler in this example is a lambda function, we could also capture the scheduler and run the timer coroutine in the IRQ handler. static const auto handler = [&amp;](void) { ... schedule_by_delay&lt;mtimer_clock&gt; now; auto [pending, next_wake] = scheduler.update(now); }; Building and running with Platform IO The example can be built and run using Platform IO. The default RISC-V platforms use an old version of GCC that does not support C++20, so a custom virtual platform configured to use xPack 12.2.0-3 riscv-none-elf-gcc and run on QEMU has been created in platformio/platforms/virt_riscv. build_flags = -std=c++20 -O2 -g -Wall -ffunction-sections -fcoroutines -fno-exceptions -fno-rtti -fno-nonansi-builtins -fno-use-cxa-atexit -fno-threadsafe-statics -nostartfiles -Wl,-Map,c-hardware-access-riscv.map The debug sequence shows entering the function example_simple(), initializing scheduler_delay&lt;mtimer_clock&gt; scheduler; then calling periodic(scheduler, 100ms, resume_simple);. Once the statement co_await scheduled_delay{ scheduler, period }; is reached the context returns to example_simple(). Then when auto [pending, next_wake] = scheduler.resume(now); is called it returns to the for loop in periodic(). The coroutine handle is stored in the scheduler class by the first call to co_await. The following call to scheduler.resume() looks up the pending coroutine handle and calls resume on the handle. The stack of the coroutine periodic() before resume can be seen below. It’s called from example_simple(). The stack of the coroutines periodic() after resume can be seen below. It’s called from coroutine_handle::resume, which is called from scheduler_ordered::resume. The stack of example_simple() function calling resume() is also on the same stack. Building with CMake and running with Spike The Makefile has targets to build with CMake. $ make target cmake \ -DCMAKE_TOOLCHAIN_FILE=cmake/riscv.cmake \ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ -B build_target \ -S . cmake --build build_target --verbose The Makefile also has targets to simulate and trace with the standard RISC-V ISA simulator, spike. The forked spike with VCD tracing is used. The forked spike is included in a docker container. $ make spike_sim docker run \ -it \ --rm \ -v .:/project \ fiveembeddev/forked_riscv_spike_dev_env:latest \ /opt/riscv-isa-sim/bin/spike \ --log=spike_sim.log \ --isa=rv32imac_zicsr \ -m0x8000000:0x2000,0x80000000:0x4000,0x20010000:0x6a120 \ --priv=m \ --pc=0x20010000 \ --vcd-log=spike_sim.vcd \ --max-cycles=10000000 \ --trace-var=timestamp_simple --trace-var=timestamp_resume --trace-var=timestamp_resume_0 --trace-var=timestamp_resume_1 --trace-var=timestamp_resume_2 --trace-var=timestamp_resume_3 --trace-var=timestamp_resume_4 --trace-var=timestamp_resume_5 --trace-var=timestamp_resume_6 --trace-var=timestamp_resume_7 --trace-var=timestamp_resume_8 --trace-var=timestamp_resume_9 --trace-var=resume_simple \ build_target/src/main.elf docker run \ --rm \ -v .:/project \ fiveembeddev/riscv_gtkwave_base:latest \ vcd2fst spike_sim.vcd spike_sim.fst The results can be viewed with GTKWave. The GTKWave savefile includes address decode and opcode decode by using docker images containing the decoders. gtkwave spike_sim.fst spike_sim.gtkw The benefit of tracing results from the ISA is that it is easy to confirm the periodic timing of the coroutine. (For this example the parameter to periodic() was changed to 1ms). The periodic write to resume_count and timestamp_resume is traced to VCD so the exact timing of the coroutine execution is visible. Using GTKWave the context switch can also be examined in detail. In the fake 1GhZ clock used by spike, the context switch takes 104ns. Coroutine runtime The runtime is described in detail in this post. The runtime for this example is in the header embeddev_coro.hpp, and it uses the embeddev_riscv.hpp header to provide a simple HAL for RISC-V and host emulation. Summary This post describes a simple working example of how to use C++ coroutines in an embedded context. The example and context are not meant to be a realistic use case, but the simplest possible use case that involves and interrupt handler and a context switch. However, the example can be built on to explore portable and lightweight asynchronous programming techniques. Future posts will look at that topic. The C++23 standard library provides a limited runtime for coroutines generators. &#8617;]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Update to Docker</title><link href="https://www.five-embeddev.com//toolchain/2024/06/15/docker/" rel="alternate" type="text/html" title="Update to Docker" /><published>2024-06-15T00:00:00+00:00</published><updated>2024-06-15T00:00:00+00:00</updated><id>https://www.five-embeddev.com//toolchain/2024/06/15/docker</id><content type="html" xml:base="https://www.five-embeddev.com//toolchain/2024/06/15/docker/"><![CDATA[<h1 id="docker-images">Docker Images</h1>

<p>The reference docker images for building and simulating examples from this blog have been updated:</p>

<p><a href="https://github.com/five-embeddev/build-and-verify/tree/main/docker">https://github.com/five-embeddev/build-and-verify/tree/main/docker</a></p>

<table>
  <thead>
    <tr>
      <th>Target</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>riscv-tool-build</td>
      <td>Base image for compiling tools</td>
    </tr>
    <tr>
      <td>riscv-spike</td>
      <td>RISC-V ISA Simulator.</td>
    </tr>
    <tr>
      <td>riscv-openocd</td>
      <td>OpenOCD JTAG debugger interface</td>
    </tr>
    <tr>
      <td>riscv-xpack-gcc</td>
      <td>X-PACK packaged GCC</td>
    </tr>
    <tr>
      <td>riscv-spike-debug-sim</td>
      <td>RISC-V Spike ISA configured to run with OpenOCD</td>
    </tr>
    <tr>
      <td>riscv-spike-debug-gdb</td>
      <td>RISC-V Spike ISA configured to run with OpenOCD &amp; GDB</td>
    </tr>
    <tr>
      <td>riscv-rust</td>
      <td>Rust for RISC-V</td>
    </tr>
    <tr>
      <td>riscv-gnu-toolchain</td>
      <td>Build GCC for RISC-V</td>
    </tr>
    <tr>
      <td>riscv-gnu-toolchain-2</td>
      <td>Build GCC for RISC-V (using docker compose to conserve resources)</td>
    </tr>
  </tbody>
</table>

<h1 id="docker-examples">Docker Examples</h1>

<p>Examples of usage:</p>

<p><a href="https://github.com/five-embeddev/build-and-verify/tree/main/examples">https://github.com/five-embeddev/build-and-verify/tree/main/examples</a></p>

<table>
  <thead>
    <tr>
      <th>Example</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>build-c/</td>
      <td>Build a simple C test program for host and target</td>
    </tr>
    <tr>
      <td>build-rust/</td>
      <td>Build a small rust program</td>
    </tr>
    <tr>
      <td>build-run-sim/</td>
      <td>Build a full baremetal example and simulate with Spike, debug with GDB</td>
    </tr>
    <tr>
      <td>test-code-c/</td>
      <td>Small example program</td>
    </tr>
  </tbody>
</table>

<h1 id="build-with-cmake">Build with Cmake</h1>

<p>e.g. <a href="https://github.com/five-embeddev/build-and-verify/tree/main/examples/build-run-sim">https://github.com/five-embeddev/build-and-verify/tree/main/examples/build-run-sim</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run \
    --rm \
    -v .:/project \
    -v /home/five/five-embeddev-wsl/build-and-verify/examples/build-run-sim/../test-code-c:/project/test_code \
        fiveembeddev/riscv_xpack_gcc_dev_env:latest \
        cmake \
                -S test_code \
                -B build \
                -G "Unix Makefiles" \
                -DCMAKE_TOOLCHAIN_FILE=../test_code/riscv.cmake
docker run \
    --rm \
    -v .:/project \
    -v /home/five/five-embeddev-wsl/build-and-verify/examples/build-run-sim/../test-code-c:/project/test_code \
        fiveembeddev/riscv_xpack_gcc_dev_env:latest \
                make \
                        VERBOSE=1 \
                        -C build
</code></pre></div></div>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="toolchain" /><category term="toolchain" /><category term="vcd" /><category term="simulation" /><category term="debugging" /><category term="tracing" /><category term="baremetal" /><category term="C" /><category term="gtkwave" /><summary type="html"><![CDATA[Docker Images The reference docker images for building and simulating examples from this blog have been updated: https://github.com/five-embeddev/build-and-verify/tree/main/docker Target Description riscv-tool-build Base image for compiling tools riscv-spike RISC-V ISA Simulator. riscv-openocd OpenOCD JTAG debugger interface riscv-xpack-gcc X-PACK packaged GCC riscv-spike-debug-sim RISC-V Spike ISA configured to run with OpenOCD riscv-spike-debug-gdb RISC-V Spike ISA configured to run with OpenOCD &amp; GDB riscv-rust Rust for RISC-V riscv-gnu-toolchain Build GCC for RISC-V riscv-gnu-toolchain-2 Build GCC for RISC-V (using docker compose to conserve resources) Docker Examples Examples of usage: https://github.com/five-embeddev/build-and-verify/tree/main/examples Example Description build-c/ Build a simple C test program for host and target build-rust/ Build a small rust program build-run-sim/ Build a full baremetal example and simulate with Spike, debug with GDB test-code-c/ Small example program Build with Cmake e.g. https://github.com/five-embeddev/build-and-verify/tree/main/examples/build-run-sim docker run \ --rm \ -v .:/project \ -v /home/five/five-embeddev-wsl/build-and-verify/examples/build-run-sim/../test-code-c:/project/test_code \ fiveembeddev/riscv_xpack_gcc_dev_env:latest \ cmake \ -S test_code \ -B build \ -G "Unix Makefiles" \ -DCMAKE_TOOLCHAIN_FILE=../test_code/riscv.cmake docker run \ --rm \ -v .:/project \ -v /home/five/five-embeddev-wsl/build-and-verify/examples/build-run-sim/../test-code-c:/project/test_code \ fiveembeddev/riscv_xpack_gcc_dev_env:latest \ make \ VERBOSE=1 \ -C build]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">GTKWave Filters for Debugging VCD Traces of RISC-V Software</title><link href="https://www.five-embeddev.com//code/2024/04/21/gtkwave/" rel="alternate" type="text/html" title="GTKWave Filters for Debugging VCD Traces of RISC-V Software" /><published>2024-04-21T00:00:00+00:00</published><updated>2024-04-21T00:00:00+00:00</updated><id>https://www.five-embeddev.com//code/2024/04/21/gtkwave</id><content type="html" xml:base="https://www.five-embeddev.com//code/2024/04/21/gtkwave/"><![CDATA[<p>GTKWave can be used for firmware debugging. This is obviously useful for co-simulation, but is also useful for debugging timing and integration problems.</p>

<p>In a <a href="/code/2022/09/04/vcd-trace/">previous post</a> I showed how to create a vcd trace of a firmware run via a fork of the RISC-V ISA simulator.</p>

<p>In this post using <a href="https://github.com/five-embeddev/riscv-gtkwave">https://github.com/five-embeddev/riscv-gtkwave</a> I add:</p>

<ul>
  <li>Instruction Decoding according to the Spike disassembler.</li>
  <li>Bus Address decoding according to an elf file.</li>
  <li>CSR register decoding.</li>
</ul>

<p>There are a few methods to decode via GTKWave. The decoders in <a href="https://github.com/five-embeddev/riscv-gtkwave">https://github.com/five-embeddev/riscv-gtkwave</a> are using 3 methods:</p>

<ul>
  <li>Generated Translate filter file.</li>
  <li>Translate Filter process.</li>
  <li>Signal Grouping and Vector Expansion and Combination</li>
</ul>

<!--more-->

<h2 id="generated-translate-filter-file">Generated Translate Filter File.</h2>

<p>The <a href="https://github.com/five-embeddev/riscv-gtkwave/blob/main/scripts/generate_csrs.py">generate_csrs.py</a> script uses <a href="https://pyvcd.readthedocs.io/en/latest/vcd.gtkw.html">pyvcd</a> to generate enumeration files such as <a href="https://github.com/five-embeddev/riscv-gtkwave/blob/main/csr_data/mcause.exception_code.gtkw">mcause.exception_code.gtkw</a> from YAML CSR data <a href="https://github.com/five-embeddev/riscv-isa-data/blob/master/csr.yaml">csr.yaml</a>.</p>

<h2 id="filter-process">Filter Process</h2>

<h3 id="risc-v-isa-disassembly-for-gtkwave">RISC-V ISA Disassembly for GTKWave</h3>

<p>The <a href="https://github.com/five-embeddev/riscv-gtkwave/blob/main/src/decode_inst.cpp">decode_inst</a> program uses the disassembler defined by the RISC-V ISA Simulator in <a href="https://github.com/riscv-software-src/riscv-isa-sim/blob/3192ee4d31f481e84281a24d55bb6130e3743668/riscv/disasm.h#L4">disasm.h</a> to disassemble instruction words.</p>

<p>It also uses <a href="https://github.com/riscv-software-src/riscv-isa-sim/blob/3192ee4d31f481e84281a24d55bb6130e3743668/riscv/isa_parser.h">isa_parser</a> to determine the ISA to dissassemble. The filename determines the ISA (via <code class="language-plaintext highlighter-rouge">argv[0]</code>, e.g. <code class="language-plaintext highlighter-rouge">decode_inst-rv32ic</code> or <code class="language-plaintext highlighter-rouge">decode_inst-rv64g</code>.</p>

<p>e.g. the <code class="language-plaintext highlighter-rouge">opcodes.hex</code> file:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>20010ae2
20010ae6
20010ae8
20010aea
20010aee
20010af0
20010af2
20010af4
20010af6
20010afa
</code></pre></div></div>

<p>Piped into <code class="language-plaintext highlighter-rouge">decode_inst</code> gives the following result.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat opcodes.hex | ./decode_inst 
auipc   t0, 0x0
c.add   a3, t0
c.mv    t0, ra
jalr    ra, a3, -88
c.mv    ra, t0
c.addi  a5, -16
c.sub   a4, a5
c.add   a2, a5
bgeu    t1, a2, pc - 120
c.j     pc - 152
</code></pre></div></div>

<h3 id="elf-symbol-lookup-for-gtkwave">ELF Symbol Lookup for GTKWave</h3>

<p>The <a href="https://github.com/five-embeddev/riscv-gtkwave/blob/main/src/decode_addr.cpp">decode_addr</a> program converts hex address values to symbols names. It uses the global variable symbols or function symbols.</p>

<p>It is derived from <a href="https://github.com/riscv-software-src/riscv-isa-sim/blob/3192ee4d31f481e84281a24d55bb6130e3743668/fesvr/elfloader.cc">elfloader.cc</a> in the RISC-V ISA simulator.</p>

<p>As GTKWave cannot pass command line arguments <code class="language-plaintext highlighter-rouge">DECODE_ELF</code> must be set as an environment variable with the path to the ELF file to use.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cd src/
$ DECODE_ELF=../example/main.elf ./decode_addr
0
-
0000
-
0000000020010000
_enter
0000000080000008
mti_count
</code></pre></div></div>

<h2 id="signal-grouping-and-vector-expansion-and-combination">Signal Grouping and Vector Expansion and Combination</h2>

<p>Registers signal vectors are expanded into bits, and fields combined as groups of bits.</p>

<p>The <a href="https://pyvcd.readthedocs.io/en/latest/vcd.gtkw.html">pyvcd</a>
class <code class="language-plaintext highlighter-rouge">GTKWSave</code> provides <code class="language-plaintext highlighter-rouge">trace_bits</code> for this. However it only
provides a single bit function <code class="language-plaintext highlighter-rouge">trace_bit</code>.</p>

<p>The <a href="https://github.com/five-embeddev/riscv-gtkwave/blob/main/scripts/generate_csrs.py">generate_csrs.py</a> script subclasses <code class="language-plaintext highlighter-rouge">GTKWSave</code> and adds <code class="language-plaintext highlighter-rouge">trace_bit_group</code> for multibit fields. These can be interpreted via translate filter files.</p>

<p>Added <code class="language-plaintext highlighter-rouge">trace_bit_group</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class UpdateGTKWSave(vcd.gtkw.GTKWSave):
    def trace_bit_group(
        self,
        lsb: int, msb: int,
        name: str,
        alias: str,
        color: Optional[Union[vcd.gtkw.GTKWColor, str, int]] = None,
        translate_filter_file: Optional[str] = None,
    ) -&gt; None:
</code></pre></div></div>

<p>Iterate over fields in a register and add the <code class="language-plaintext highlighter-rouge">trace_bit</code> or <code class="language-plaintext highlighter-rouge">trace_bit_group</code>. There is one save file per CSR.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   gtkw = UpdateGTKWSave(rout)
   gtkw.comment(f"Fields for register {reg_name}")
   gtkw.begin_group(reg_name)
   with gtkw.trace_bits(reg_path):
       for fkey, fdata in reg_data['fields'].items():
           gtkw.comment(f"Field {reg_name}_{fkey}")
           fwidth=get_field_width(fdata)
           msb,lsb=get_field_range(fdata)
           if fwidth==1:
               gtkw.trace_bit(reg_width-lsb-1, 
                   name=f"{reg_path}",
                   alias=f"{reg_name}_{fkey}")
           else:
               translate_filter_file=...
               gtkw.trace_bit_group(
                   reg_width-msb-1, 
                   reg_width-lsb, 
                   name=f"{reg_path}",
                   alias=f"{reg_name}_{fkey}",
                   translate_filter_file=translate_filter_file)
   gtkw.end_group(reg_name)

</code></pre></div></div>

<h2 id="firmware-trace-example">Firmware Trace Example</h2>

<p><img src="/images/vcd-wave-int.png" alt="Example of firmware trace" /></p>

<h2 id="todo">TODO</h2>

<p>There are many limitations to these programs.</p>

<ul>
  <li>64bit/32bit distinctions are not</li>
</ul>

<p>In a real world application other decoders that are useful are:</p>

<ul>
  <li>MMIO Peripheral register decoding.</li>
  <li>External bus/device (I2C/SPI) register decoding.</li>
  <li>RTOS/OS instrumentation (e.g. current task, current memory map etc)</li>
  <li>Test name/assertion name.</li>
  <li>etc.</li>
</ul>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="code" /><category term="toolchain" /><category term="vcd" /><category term="simulation" /><category term="debugging" /><category term="tracing" /><category term="baremetal" /><category term="C" /><category term="gtkwave" /><summary type="html"><![CDATA[GTKWave can be used for firmware debugging. This is obviously useful for co-simulation, but is also useful for debugging timing and integration problems. In a previous post I showed how to create a vcd trace of a firmware run via a fork of the RISC-V ISA simulator. In this post using https://github.com/five-embeddev/riscv-gtkwave I add: Instruction Decoding according to the Spike disassembler. Bus Address decoding according to an elf file. CSR register decoding. There are a few methods to decode via GTKWave. The decoders in https://github.com/five-embeddev/riscv-gtkwave are using 3 methods: Generated Translate filter file. Translate Filter process. Signal Grouping and Vector Expansion and Combination]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RISC-V ISA Instruction Quick Reference Update</title><link href="https://www.five-embeddev.com//updates/2024/03/03/instructions/" rel="alternate" type="text/html" title="RISC-V ISA Instruction Quick Reference Update" /><published>2024-03-03T00:00:00+00:00</published><updated>2024-03-03T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2024/03/03/instructions</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2024/03/03/instructions/"><![CDATA[<p>The <a href="/quickref/instructions.html">Instructions Quickref</a> has been updated to match <a href="/updates/2023/11/15/compiler-explorer/">the changes made for Compiler Explorer</a>.</p>

<p>The opcode references are generated from <a href="https://five-embeddev.github.io/riscv-docs-html/opcodes.yaml">opcodes.yaml</a> which is generated by <a href="https://github.com/five-embeddev/riscv-docs-html/blob/gh_pages/generators/scripts/convert_opcodes.rb">convert_opcodes.rb</a> in <a href="https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages">https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages</a>.</p>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="riscv.org" /><category term="spec" /><category term="html" /><summary type="html"><![CDATA[The Instructions Quickref has been updated to match the changes made for Compiler Explorer. The opcode references are generated from opcodes.yaml which is generated by convert_opcodes.rb in https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RISC-V ISA Reference for Compiler Explorer</title><link href="https://www.five-embeddev.com//updates/2023/11/15/compiler-explorer/" rel="alternate" type="text/html" title="RISC-V ISA Reference for Compiler Explorer" /><published>2023-11-15T00:00:00+00:00</published><updated>2023-11-15T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2023/11/15/compiler-explorer</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2023/11/15/compiler-explorer/"><![CDATA[<p>Compiler explorer now includes RISC-V opcode references for the rv64 targets, following my pull <a href="https://github.com/compiler-explorer/compiler-explorer/pull/5598/commits">pull request</a> and initial work by <a href="https://github.com/SiyaoIsHiding">Siyao</a>.</p>

<p>The updated files are:</p>
<ul>
  <li><a href="https://github.com/compiler-explorer/compiler-explorer/blob/main/etc/scripts/docenizers/docenizer-riscv64.py">etc/scripts/docenizers/docenizer-riscv64.py</a>: Generator, converts <code class="language-plaintext highlighter-rouge">yaml</code> to <code class="language-plaintext highlighter-rouge">ts</code>.</li>
  <li><a href="https://github.com/compiler-explorer/compiler-explorer/blob/main/lib/asm-docs/generated/asm-docs-riscv64.ts">lib/asm-docs/generated/asm-docs-riscv64.ts</a>: The generated opcode documentation.</li>
</ul>

<p>The opcode references are generated from <a href="https://five-embeddev.github.io/riscv-docs-html/opcodes.yaml">opcodes.yaml</a> which is generated by <a href="https://github.com/five-embeddev/riscv-docs-html/blob/gh_pages/generators/scripts/convert_opcodes.rb">convert_opcodes.rb</a> in <a href="https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages">https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages</a>.</p>

<p>The opcode decriptions are automatically extracted from the ISA user manual in HTML format. For example the description for <a href="https://godbolt.org/api/asm/riscv64/sd">sd</a> is extracted from the <a href="https://five-embeddev.github.io/riscv-docs-html//riscv-user-isa-manual/Priv-v1.12/rv64.html#load-and-store-instructions">Load and Store Instructions</a> section.</p>

<p>To generate the data run these commands in the compiler explorer repo:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> cd compiler-explorer/etc/scripts/docenizers/
./docenizer-riscv64.py \
    -i https://five-embeddev.github.io/riscv-docs-html/opcodes.yaml \
    -o ../../../lib/asm-docs/generated/asm-docs-riscv64.ts
</code></pre></div></div>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="riscv.org" /><category term="spec" /><category term="html" /><summary type="html"><![CDATA[Compiler explorer now includes RISC-V opcode references for the rv64 targets, following my pull pull request and initial work by Siyao. The updated files are: etc/scripts/docenizers/docenizer-riscv64.py: Generator, converts yaml to ts. lib/asm-docs/generated/asm-docs-riscv64.ts: The generated opcode documentation. The opcode references are generated from opcodes.yaml which is generated by convert_opcodes.rb in https://github.com/five-embeddev/riscv-docs-html/tree/gh_pages. The opcode decriptions are automatically extracted from the ISA user manual in HTML format. For example the description for sd is extracted from the Load and Store Instructions section. To generate the data run these commands in the compiler explorer repo: cd compiler-explorer/etc/scripts/docenizers/ ./docenizer-riscv64.py \ -i https://five-embeddev.github.io/riscv-docs-html/opcodes.yaml \ -o ../../../lib/asm-docs/generated/asm-docs-riscv64.ts]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RISC-V ISA Manuals to HTML</title><link href="https://www.five-embeddev.com//updates/2023/07/31/html-docs/" rel="alternate" type="text/html" title="RISC-V ISA Manuals to HTML" /><published>2023-07-31T00:00:00+00:00</published><updated>2023-07-31T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2023/07/31/html-docs</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2023/07/31/html-docs/"><![CDATA[<p>The <a href="/riscv-isa-manual/latest/riscv-spec.html">User ISA</a> and <a href="/riscv-isa-manual/latest/riscv-privileged.html">Privileged ISA</a> have been are converted to HTML from TEX on the <a href="https://github.com/riscv/riscv-isa-manual">upstream repo</a>.</p>

<p>The latest upstream versions have recently been converted to
<a href="https://asciidoc.org/">asciidoc</a>, but the legacy versions that were
used to generate HTML on this site are converted from the older TEX.</p>

<p>In theory converting tex should be as simple as running a conversion tool such
as <a href="https://pandoc.org/">pandoc</a> or
<a href="https://www.latex2html.org/">latex2html</a> but without some
optimization that gives quite ugly results - in particular for tables
and diagrams.</p>

<p>The set of scripts that this blog uses to optimize the pandoc output
has been uploaded to
<a href="https://github.com/five-embeddev/riscv-docs-html">https://github.com/five-embeddev/riscv-docs-html</a>. A Dockerfile is
used to configure a build environment with pandoc and asciidoctor and
the other tools used to do the conversion.</p>

<p>A set of static html github pages have been exported here,
<a href="https://five-embeddev.github.io/riscv-docs-html/">https://five-embeddev.github.io/riscv-docs-html/</a>. They HTML is
identical, but it lacks the side navigation menu and css style used by
this site.</p>

<p>These scripts are also used to create the
<a href="https://github.com/five-embeddev/riscv-isa-data/blob/master/opcodes.yaml">opcodes.yaml</a>
and
<a href="https://github.com/five-embeddev/riscv-isa-data/blob/master/csr.yaml">csr.yaml</a>
data files used by the <a href="/quickref/instructions.html">Instruction Quick
Reference</a> and <a href="/quickref/csrs.html">CSR Quick
Reference</a> on this site.</p>

<p>More info on that will be posted later.</p>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="riscv.org" /><category term="spec" /><category term="html" /><category term="docker" /><summary type="html"><![CDATA[The User ISA and Privileged ISA have been are converted to HTML from TEX on the upstream repo. The latest upstream versions have recently been converted to asciidoc, but the legacy versions that were used to generate HTML on this site are converted from the older TEX. In theory converting tex should be as simple as running a conversion tool such as pandoc or latex2html but without some optimization that gives quite ugly results - in particular for tables and diagrams. The set of scripts that this blog uses to optimize the pandoc output has been uploaded to https://github.com/five-embeddev/riscv-docs-html. A Dockerfile is used to configure a build environment with pandoc and asciidoctor and the other tools used to do the conversion. A set of static html github pages have been exported here, https://five-embeddev.github.io/riscv-docs-html/. They HTML is identical, but it lacks the side navigation menu and css style used by this site. These scripts are also used to create the opcodes.yaml and csr.yaml data files used by the Instruction Quick Reference and CSR Quick Reference on this site. More info on that will be posted later.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RISC-V Interrupts/Exceptions Quick Reference</title><link href="https://www.five-embeddev.com//updates/2023/05/11/interrupts/" rel="alternate" type="text/html" title="RISC-V Interrupts/Exceptions Quick Reference" /><published>2023-05-11T00:00:00+00:00</published><updated>2023-05-11T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2023/05/11/interrupts</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2023/05/11/interrupts/"><![CDATA[<p>A fix has been made to the <a href="/quickref/interrupts.html">Interrupts/Exceptions Quick Reference</a> to clarify that <span title="Machine status register."><a href="/riscv-priv-isa-manual/Priv-v1.12/machine.html#machine-status-registers-mstatus-and-mstatush"><b>mstatus</b></a></span>.<b>mpp</b> is set to the the least-privileged supported mode on <b><tt><a href="/riscv-priv-isa-manual/Priv-v1.12/machine.html#privstack">mret</a></tt></b>. The local version of <a href="/riscv-isa-manual/latest/machine.html">Machine-Level ISA</a> has been updated to the <a href="https://github.com/riscv/riscv-isa-manual/blob/Priv-v1.12/src/machine.tex">Priv-v1.12 source</a> as this is the current version on <a href="https://riscv.org/technical/specifications/">https://riscv.org/technical/specifications/</a>. References to the depreciated user mode interrupts and the n extension were also removed.</p>

<!--more-->

<p>From <span title="Machine status register."><a href="/riscv-priv-isa-manual/Priv-v1.12/machine.html#machine-status-registers-mstatus-and-mstatush"><b>mstatus</b></a></span>:</p>

<blockquote>
  <p>An MRET or SRET instruction is used to return from a trap in M-mode or S-mode respectively. When executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the privilege mode is changed to y; xPIE is set to 1; and xPP is set to the least-privileged supported mode (U if U-mode is implemented, else M). If xPP≠M, xRET also sets MPRV=0.</p>
</blockquote>

<p>From <span title="Supervisor status register."><a href="/riscv-priv-isa-manual/Priv-v1.12/supervisor.html#sstatus"><b>sstatus</b></a></span>:</p>

<blockquote>
  <p>When an SRET instruction (see Section [otherpriv]) is executed to return from the trap handler, the privilege level is set to user mode if the SPP bit is 0, or supervisor mode if the SPP bit is 1; SPP is then set to 0.
11</p>
</blockquote>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="interrupts" /><category term="quickref" /><summary type="html"><![CDATA[A fix has been made to the Interrupts/Exceptions Quick Reference to clarify that mstatus.mpp is set to the the least-privileged supported mode on mret. The local version of Machine-Level ISA has been updated to the Priv-v1.12 source as this is the current version on https://riscv.org/technical/specifications/. References to the depreciated user mode interrupts and the n extension were also removed.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">CSR Quick Reference Update</title><link href="https://www.five-embeddev.com//updates/2023/05/04/csr-update/" rel="alternate" type="text/html" title="CSR Quick Reference Update" /><published>2023-05-04T00:00:00+00:00</published><updated>2023-05-04T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2023/05/04/csr-update</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2023/05/04/csr-update/"><![CDATA[<p>The <a href="/quickref/csrs.html">CSR quick reference</a> list was updated:</p>

<ul>
  <li>Cross references to manuals.</li>
  <li>Details for some fields.</li>
</ul>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="registers" /><category term="csr" /><category term="quickref" /><summary type="html"><![CDATA[The CSR quick reference list was updated: Cross references to manuals. Details for some fields.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">RISC-V ISA Manual Update</title><link href="https://www.five-embeddev.com//updates/2023/05/03/isa-update/" rel="alternate" type="text/html" title="RISC-V ISA Manual Update" /><published>2023-05-03T00:00:00+00:00</published><updated>2023-05-03T00:00:00+00:00</updated><id>https://www.five-embeddev.com//updates/2023/05/03/isa-update</id><content type="html" xml:base="https://www.five-embeddev.com//updates/2023/05/03/isa-update/"><![CDATA[<p>The <a href="/riscv-isa-manual/latest/riscv-spec.html">User ISA</a> and <a href="/riscv-isa-manual/latest/riscv-privileged.html">Privileged ISA</a> have been updated to tag Priv-1.12 in the <a href="https://github.com/riscv/riscv-isa-manual">upstream repo</a> and re-generated as HTML.</p>

<ul>
  <li>Updated to official release branch (Priv-1.12)</li>
  <li>Added links to source documents, source version.</li>
</ul>

<p>The upstream changelog from git is:</p>

<!--more-->

<ul>
  <li>bump priv version</li>
  <li>Relax Svpbmt sequence slightly</li>
  <li>Add FLH, FSH to defined transformed instructions for H extension (<a href="https://github.com/riscv/riscv-isa-manual/issues/792">#792</a>)</li>
  <li>priv spec version 20211202</li>
  <li>boldface ratified extensions</li>
  <li>Clarify that henvcfg.PBMTE is read-only zero if Svpbmt is not implemented</li>
  <li>Clarify that PBMTE is read-only zero if Svpbmt is not implemented</li>
  <li>fix typo</li>
  <li>Permit speculative execution of HLV/HSV; reset hgatp.MODE, satp.MODE</li>
  <li>Fix Travis build status image URL</li>
  <li>Rework the description of exceptions for Svinval (<a href="https://github.com/riscv/riscv-isa-manual/issues/786">#786</a>)</li>
  <li>Change H extension to version 1.0 (<a href="https://github.com/riscv/riscv-isa-manual/issues/787">#787</a>)</li>
  <li>Interrupts and exceptions are different things. (<a href="https://github.com/riscv/riscv-isa-manual/issues/788">#788</a>)</li>
  <li>Priv specs are ratified</li>
  <li>Add menvcfg.PBMTE / henvcfg.PBMTE</li>
  <li>Remark that Svnapot and Svpbmt require Sv39</li>
  <li>Move SFENCE.VMA/satp.MODE remark to better location</li>
  <li>Clarify that implicit reads of CSRs return same value as explicit reads (<a href="https://github.com/riscv/riscv-isa-manual/issues/783">#783</a>)</li>
  <li>Fix typo</li>
  <li>Add VS field</li>
  <li>Split RV32 [v]sstatus figures into two rows</li>
  <li>Add Hazard3 to open-source marchid list (<a href="https://github.com/riscv/riscv-isa-manual/issues/784">#784</a>)</li>
  <li>Fix typo</li>
  <li>Specify a sequence to regain coherence wrt. mismatched PBMTs</li>
  <li>Avoid use of “timebase”</li>
  <li>Clarify definition of [m]time CSR</li>
  <li>Merge branch ‘jhauser-us-jhauser-2021-CSRFieldMods’</li>
  <li>Clarify when SFENCE.VMA/HFENCE.GVMA need be executed</li>
  <li>Extension is “implemented”, not “enabled” (in Svinval) (<a href="https://github.com/riscv/riscv-isa-manual/issues/780">#780</a>)</li>
  <li>Explain why mstatus.TVM doesn’t affect vsatp, HFENCE.VVMA (<a href="https://github.com/riscv/riscv-isa-manual/issues/779">#779</a>)</li>
  <li>H extension requires page-based address translation (<a href="https://github.com/riscv/riscv-isa-manual/issues/778">#778</a>)</li>
  <li>Non-normatively remark that high-order PPN bits aren’t ignored</li>
  <li>Revert “Separate transformation for HLV instructions (<a href="https://github.com/riscv/riscv-isa-manual/issues/777">#777</a>)”</li>
  <li>Fix typo</li>
  <li>Separate transformation for HLV instructions (<a href="https://github.com/riscv/riscv-isa-manual/issues/777">#777</a>)</li>
  <li>Memory access traps may write zero to stval (<a href="https://github.com/riscv/riscv-isa-manual/issues/776">#776</a>)</li>
  <li>Accesses to pages with mismatched attrs are I/O <em>and</em> memory wrt FENCE (<a href="https://github.com/riscv/riscv-isa-manual/issues/774">#774</a>)</li>
  <li>Rename hstatus.HU (<a href="https://github.com/riscv/riscv-isa-manual/issues/770">#770</a>)</li>
  <li>Allow more bits of hideleg to be writable (<a href="https://github.com/riscv/riscv-isa-manual/issues/772">#772</a>)</li>
  <li>Clarify condition when virtual instruction trap will occur (<a href="https://github.com/riscv/riscv-isa-manual/issues/773">#773</a>)</li>
  <li>CSR mideleg masks hideleg, hip, and hie (<a href="https://github.com/riscv/riscv-isa-manual/issues/771">#771</a>)</li>
  <li>Rewrite most instances of “hardwire” as “read-only” (<a href="https://github.com/riscv/riscv-isa-manual/issues/768">#768</a>)</li>
  <li>Remove trailing whitespace from a.tex (<a href="https://github.com/riscv/riscv-isa-manual/issues/767">#767</a>)</li>
  <li>Document version 20211105-signoff</li>
  <li>Further relax PMP/address-translation caching interactions</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/763">#763</a> from riscv/virt-mem-extensions</li>
  <li>Add Sv57 and Sv57x4</li>
  <li>Merge branch ‘master’ of github.com:riscv/riscv-isa-manual</li>
  <li>Back to draft status</li>
  <li>Define the Zicntr and Zihpm extensions</li>
  <li>Improve text in Zicntr section</li>
  <li>Document version 20211028-signoff</li>
  <li>Incorporate Steve’s feedback</li>
  <li>No valid LR/SC reservation upon reset</li>
  <li>Fix editing error in mtval/stval definition</li>
  <li>Clarify order in which PMP CSRs must be implemented</li>
  <li>Fix permissions of *envcfg CSRs</li>
  <li>Improve description of FENCE.TSO</li>
  <li>Add example to clarify mip.SEIP behavior</li>
  <li>Bump priv version number</li>
  <li>Priv-1.12 spec for public review</li>
  <li>JohnH is an editor of the priv spec</li>
  <li>RISC-V Foundation -&gt; RISC-V International</li>
  <li>Freeze the hypervisor extension, version 1.0.0-rc (<a href="https://github.com/riscv/riscv-isa-manual/issues/739">#739</a>)</li>
  <li>mip.MSIP and mie.MSIE may be hardwired zeros (<a href="https://github.com/riscv/riscv-isa-manual/issues/738">#738</a>)</li>
  <li>Hypervisor extension requires page-based address translation (<a href="https://github.com/riscv/riscv-isa-manual/issues/737">#737</a>)</li>
  <li>Fix apparent typo re hpmcounter*h (<a href="https://github.com/riscv/riscv-isa-manual/issues/735">#735</a>)</li>
  <li>State behavior of uncacheable accesses to cacheable locations</li>
  <li>Clarify that WARL fields contain legal values after reset (<a href="https://github.com/riscv/riscv-isa-manual/issues/734">#734</a>)</li>
  <li>Rename STCE to STCD to reverse its polarity</li>
  <li>Generalize SSIP to support forthcoming interrupt controllers (<a href="https://github.com/riscv/riscv-isa-manual/issues/726">#726</a>)</li>
  <li>Speculative implicit reads, v2 (<a href="https://github.com/riscv/riscv-isa-manual/issues/724">#724</a>)</li>
  <li>Fix a typo in Figure A.13. (<a href="https://github.com/riscv/riscv-isa-manual/issues/733">#733</a>)</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/727">#727</a> from riscv/mseccfg</li>
  <li>Make virtual instruction exceptions more consistent for VU mode (<a href="https://github.com/riscv/riscv-isa-manual/issues/730">#730</a>)</li>
  <li>Pedantically clarify behavior of writing lo/hi parts of counters</li>
  <li>Remove errant preface entry</li>
  <li>Clarify widths of privileged CSRs (<a href="https://github.com/riscv/riscv-isa-manual/issues/728">#728</a>)</li>
  <li>Revert “Replace “EEI” with “execution environment” (<a href="https://github.com/riscv/riscv-isa-manual/issues/723">#723</a>)”</li>
  <li>Add preface entry</li>
  <li>Designate some of SYSTEM opcode for custom use</li>
  <li>Add mconfigptr CSR (<a href="https://github.com/riscv/riscv-isa-manual/issues/697">#697</a>)</li>
  <li>Replace “EEI” with “execution environment” (<a href="https://github.com/riscv/riscv-isa-manual/issues/723">#723</a>)</li>
  <li>Fix (again) non-normative CSR side-effect text</li>
  <li>Remove historical remark on MRET definition</li>
  <li>Fix non-normative text about CSR ordering (<a href="https://github.com/riscv/riscv-isa-manual/issues/720">#720</a>)</li>
  <li>Add marchid for Hummingbirdv2 E203 (<a href="https://github.com/riscv/riscv-isa-manual/issues/664">#664</a>)</li>
  <li>Update H chapter table of synchronous exception priorities (<a href="https://github.com/riscv/riscv-isa-manual/issues/717">#717</a>)</li>
  <li>Tweak table of synchronous exception priorities (<a href="https://github.com/riscv/riscv-isa-manual/issues/716">#716</a>)</li>
  <li>Make explicit the priorities of synch. exceptions of H extension (<a href="https://github.com/riscv/riscv-isa-manual/issues/711">#711</a>)</li>
  <li>Clarify priorities of synchronous exceptions (<a href="https://github.com/riscv/riscv-isa-manual/issues/715">#715</a>)</li>
  <li>stval already cannot be zero on breakpoints, misaligned addresses (<a href="https://github.com/riscv/riscv-isa-manual/issues/714">#714</a>)</li>
  <li>VS mode should not see exception code 10 (<a href="https://github.com/riscv/riscv-isa-manual/issues/712">#712</a>)</li>
  <li>Merge branch ‘jhauser-us-jhauser-2021-HBaseI’</li>
  <li>Corrections to mstatus in hypervisor chapter (<a href="https://github.com/riscv/riscv-isa-manual/issues/710">#710</a>)</li>
  <li>Minor improvements to text for virtual instruction exceptions (<a href="https://github.com/riscv/riscv-isa-manual/issues/709">#709</a>)</li>
  <li>Clarify when mstatus.FS may be hardwired zero (<a href="https://github.com/riscv/riscv-isa-manual/issues/707">#707</a>)</li>
  <li>Interrupt conditions are also evaluated on falling edges</li>
  <li>Generalize interrupt trap condition evaluation conditions (<a href="https://github.com/riscv/riscv-isa-manual/issues/705">#705</a>)</li>
  <li>Clarify that RV64 accesses to mtime[cmp] are atomic</li>
  <li>State that misa.F does not affect mstatus.FS</li>
  <li>Improve rules for virtual instruction exceptions, again (<a href="https://github.com/riscv/riscv-isa-manual/issues/703">#703</a>)</li>
  <li>Clarify mepc invalid address conversion</li>
  <li>Improve description of interrupt traps (<a href="https://github.com/riscv/riscv-isa-manual/issues/701">#701</a>)</li>
  <li>Merge branch ‘jhauser-us-jhauser-2021-extStats’</li>
  <li>Clarify that SFENCE.VMA isn’t required for Sbare</li>
  <li>Fix b6cade07034d39e65134a879a5c3369d50e0df0e</li>
  <li>Remove N extension chapter for now</li>
  <li>CSR instead of field (<a href="https://github.com/riscv/riscv-isa-manual/issues/669">#669</a>)</li>
  <li>Add non-normative text about VIPT caches not being exposed</li>
  <li>Remove concept of hard reset from normative text</li>
  <li>Add marchid for XiangShan (<a href="https://github.com/riscv/riscv-isa-manual/issues/661">#661</a>)</li>
  <li>PMP RWX are collectively WARL, with R=0 W=1 being illegal (<a href="https://github.com/riscv/riscv-isa-manual/issues/658">#658</a>)</li>
  <li>Delete detailed text surrounding RVC immediates</li>
  <li>Merge branch ‘jscheid-ventana-jscheid/c-pmas’</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/655">#655</a> from riscv/remove-l-t</li>
  <li>Clarifying FENCE operation behavior for external devices. (<a href="https://github.com/riscv/riscv-isa-manual/issues/657">#657</a>)</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/652">#652</a> from rafaelcalcada/master</li>
  <li>Use plural “base ISAs” rather than “base ISA” when appropriate</li>
  <li>Fix capitalization of HINTs</li>
  <li>Fix hyphenation</li>
  <li>Clarify need for HFENCE.GVMA after hgatp.MODE change</li>
  <li>Assin version number 0.1 to Zmmul</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/648">#648</a> from riscv/zmmul</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/645">#645</a> from riscv/listofitems</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/644">#644</a> from EwoutH/patch-1</li>
  <li>marchid request for RudolV (<a href="https://github.com/riscv/riscv-isa-manual/issues/643">#643</a>)</li>
  <li>Minor mstatus and sstatus layout edits. (<a href="https://github.com/riscv/riscv-isa-manual/issues/642">#642</a>)</li>
  <li>SUM should be hardwired to 0 for cores without paging (<a href="https://github.com/riscv/riscv-isa-manual/issues/641">#641</a>)</li>
  <li>Requesting marchid for cv32e40x and cv32e40s (<a href="https://github.com/riscv/riscv-isa-manual/issues/630">#630</a>)</li>
  <li>marchid request for Ibex (<a href="https://github.com/riscv/riscv-isa-manual/issues/638">#638</a>)</li>
  <li>Define canonical location of K extension in ISA string</li>
  <li>Clarify hypervisor privilege hierarchy/global interrupt enables</li>
  <li>Add FENCE.TSO and PAUSE to RV32I instruction table</li>
  <li>Clarify that AMOs use the original address when rd == rs1 (<a href="https://github.com/riscv/riscv-isa-manual/issues/632">#632</a>)</li>
  <li>fix typo in preface</li>
  <li>s/NSE/Custom/ in RVC spec</li>
  <li>wrap long line</li>
  <li>Merge pull request <a href="https://github.com/riscv/riscv-isa-manual/issues/398">#398</a> from riscv/pause</li>
  <li>Update preface</li>
  <li>Clarify type of timer interrupt (<a href="https://github.com/riscv/riscv-isa-manual/issues/617">#617</a>)</li>
  <li>Fix editing error introduced in 9ff515cd6695ac392e5ca32b73a135aa197e2778</li>
  <li>Delete duplicate (and now inconsistent) version number given in body text. Closes <a href="https://github.com/riscv/riscv-isa-manual/issues/618">#618</a>.</li>
  <li>Revert “should -&gt; shall in definition of 0 instruction”</li>
  <li>should -&gt; shall in definition of 0 instruction</li>
  <li>Explain rationale for seting xPP=U on an xRET</li>
  <li>Add preface note that N extension was moved to its own chapter</li>
  <li>Clean up NMI/mepc wording</li>
  <li>Additional FS clarification</li>
  <li>Clarify rm field on widening conversions (<a href="https://github.com/riscv/riscv-isa-manual/issues/619">#619</a>)</li>
  <li>spell check</li>
  <li>clarify that FS need only be set to dirty if the state is actually changed</li>
  <li>LR/SC extension commentary tweak</li>
  <li>Clarify when FP conversions raise the Inexact flag</li>
  <li>Use consistent wording for FP exception text</li>
  <li>Make unused misa fields 0 (WARL) rather than WLRL. (<a href="https://github.com/riscv/riscv-isa-manual/issues/615">#615</a>)</li>
  <li>Dedicated section for machine-level memory-mapped registers (not standard CSRs) (<a href="https://github.com/riscv/riscv-isa-manual/issues/614">#614</a>)</li>
  <li>Clarify G bits in all G-stage PTEs are reserved (<a href="https://github.com/riscv/riscv-isa-manual/issues/613">#613</a>)</li>
  <li>PMP uses physical addresses (not effective addresses) (<a href="https://github.com/riscv/riscv-isa-manual/issues/610">#610</a>)</li>
  <li>Update editors</li>
  <li>Update contributors</li>
  <li>mcounteren is WARL</li>
  <li>PMP TOR clarifications</li>
  <li>Another attempt to clarify SEIP RMW semantics</li>
  <li>Attempt to clarify SEIP RMW semantics</li>
  <li>fm=0 for FENCE HINTs</li>
  <li>Both HWBPs and EBREAKs populate mtval (<a href="https://github.com/riscv/riscv-isa-manual/issues/601">#601</a>)</li>
  <li>marchid request for NEORV32 core (<a href="https://github.com/riscv/riscv-isa-manual/issues/579">#579</a>)</li>
</ul>]]></content><author><name>Five Embeddev</name><email>blog@five-embeddev.com</email></author><category term="updates" /><category term="riscv.org" /><category term="spec" /><summary type="html"><![CDATA[The User ISA and Privileged ISA have been updated to tag Priv-1.12 in the upstream repo and re-generated as HTML. Updated to official release branch (Priv-1.12) Added links to source documents, source version. The upstream changelog from git is:]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.five-embeddev.com//siteicon.png" /><media:content medium="image" url="https://www.five-embeddev.com//siteicon.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>