lupyuen.org/articles/mynewt.html

1000 lines
No EOL
65 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>Porting Mynewt to PineCone BL602</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Porting Mynewt to PineCone BL602"
data-rh="true">
<meta property="og:description"
content="How we port Apache Mynewt embedded operating system to the PineCone BL602 RISC-V Board"
data-rh="true">
<meta property="og:image"
content="https://lupyuen.github.io/images/mynewt-title.png">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/mynewt.html" />
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">Porting Mynewt to PineCone BL602</h1>
<nav id="rustdoc"><ul>
<li><a href="#adapt-from-existing-risc-v-port" title="Adapt from Existing RISC-V Port">1 Adapt from Existing RISC-V Port</a><ul></ul></li>
<li><a href="#set-gcc-compiler-for-risc-v" title="Set GCC Compiler for RISC-V">2 Set GCC Compiler for RISC-V</a><ul>
<li><a href="#mynewt-project-and-firmware" title="Mynewt Project and Firmware">2.1 Mynewt Project and Firmware</a><ul></ul></li></ul></li>
<li><a href="#add-microcontroller-definition" title="Add Microcontroller Definition">3 Add Microcontroller Definition</a><ul></ul></li>
<li><a href="#add-board-support-package" title="Add Board Support Package">4 Add Board Support Package</a><ul></ul></li>
<li><a href="#define-linker-script" title="Define Linker Script">5 Define Linker Script</a><ul>
<li><a href="#bootloader-image-header" title="Bootloader Image Header">5.1 Bootloader Image Header</a><ul></ul></li></ul></li>
<li><a href="#define-flash-map" title="Define Flash Map">6 Define Flash Map</a><ul>
<li><a href="#future-flash-map" title="Future Flash Map">6.1 Future Flash Map</a><ul></ul></li></ul></li>
<li><a href="#set-firmware-target" title="Set Firmware Target">7 Set Firmware Target</a><ul></ul></li>
<li><a href="#build-the-firmware" title="Build the Firmware">8 Build the Firmware</a><ul></ul></li>
<li><a href="#implement-hardware-abstraction-layer" title="Implement Hardware Abstraction Layer">9 Implement Hardware Abstraction Layer</a><ul></ul></li>
<li><a href="#implement-start-code" title="Implement Start Code">10 Implement Start Code</a><ul></ul></li>
<li><a href="#risc-v-rv32imfc-vs-rv32imac" title="RISC-V rv32imfc vs rv32imac">11 RISC-V rv32imfc vs rv32imac</a><ul></ul></li>
<li><a href="#decouple-sifive-fe310-from-rv32imac" title="Decouple SiFive FE310 from rv32imac">12 Decouple SiFive FE310 from rv32imac</a><ul></ul></li>
<li><a href="#inspect-the-firmware" title="Inspect the Firmware">13 Inspect the Firmware</a><ul></ul></li>
<li><a href="#debug-firmware-with-vscode" title="Debug Firmware with VSCode">14 Debug Firmware with VSCode</a><ul>
<li><a href="#debugging-features" title="Debugging Features">14.1 Debugging Features</a><ul></ul></li>
<li><a href="#terminating-openocd" title="Terminating OpenOCD">14.2 Terminating OpenOCD</a><ul></ul></li></ul></li>
<li><a href="#how-to-test" title="How To Test">15 How To Test</a><ul>
<li><a href="#testing-the-led" title="Testing the LED">15.1 Testing the LED</a><ul></ul></li>
<li><a href="#testing-the-jumper" title="Testing the Jumper">15.2 Testing the Jumper</a><ul></ul></li>
<li><a href="#testing-the-uart-port" title="Testing the UART Port">15.3 Testing the UART Port</a><ul></ul></li></ul></li>
<li><a href="#whats-next" title="Whats Next">16 Whats Next</a><ul></ul></li>
<li><a href="#appendix-load-firmware-to-cache-memory-not-flash-memory" title="Appendix: Load Firmware to Cache Memory, not Flash Memory">17 Appendix: Load Firmware to Cache Memory, not Flash Memory</a><ul></ul></li>
<li><a href="#appendix-install-newt" title="Appendix: Install newt">18 Appendix: Install newt</a><ul>
<li><a href="#linux-and-macos" title="Linux and macOS">18.1 Linux and macOS</a><ul></ul></li>
<li><a href="#windows" title="Windows">18.2 Windows</a><ul></ul></li></ul></li>
<li><a href="#appendix-create-the-mynewt-firmware" title="Appendix: Create the Mynewt Firmware">19 Appendix: Create the Mynewt Firmware</a><ul></ul></li>
<li><a href="#appendix-vscode-settings" title="Appendix: VSCode Settings">20 Appendix: VSCode Settings</a><ul>
<li><a href="#debugger-settings" title="Debugger Settings">20.1 Debugger Settings</a><ul></ul></li>
<li><a href="#task-settings" title="Task Settings">20.2 Task Settings</a><ul></ul></li></ul></li></ul></nav><p><img src="https://lupyuen.github.io/images/mynewt-title.png" alt="Debugging Mynewt Firmware with VSCode" /></p>
<p><em>Debugging Mynewt Firmware with VSCode</em></p>
<p>📝 <em>21 Dec 2020</em></p>
<p>Our journey so far…</p>
<ol>
<li>
<p>We took a quick peek at <a href="https://lupyuen.github.io/articles/pinecone"><strong>PineCone BL602 RISC-V Evaluation Board</strong></a></p>
</li>
<li>
<p>Then we <a href="https://lupyuen.github.io/articles/openocd"><strong>connected PineCone to OpenOCD</strong></a> with a JTAG Debugger…</p>
</li>
<li>
<p>And we <a href="https://lupyuen.github.io/articles/debug"><strong>debugged Rust on PineCone</strong></a> with VSCode and GDB</p>
</li>
</ol>
<p>Today well learn about our port of <a href="https://mynewt.apache.org/"><strong>Apache Mynewt</strong></a> embedded operating system to PineCone.</p>
<p><a href="https://youtu.be/iDS8CBplSw8">Watch the Sneak Peek on YouTube</a></p>
<p><em>Why port Mynewt to BL602?</em></p>
<p>Since FreeRTOS is already supported on BL602 (for multitasking Bluetooth LE and WiFi in the background), lets port a modern embedded operating system like Mynewt.</p>
<p>Its a great way to learn the internals of BL602. And this article will be a valuable resource for porting to BL602 other embedded operating systems, like Zephyr and RIOT.</p>
<p><a href="https://lupyuen.github.io/articles/nuttx"><strong>UPDATE: Check out Apache NuttX operating system for BL602</strong></a></p>
<p><a href="https://github.com/nandojve/zephyr/blob/bouffalo/boards/riscv/dt_bl10_devkit/doc/index.rst"><strong>UPDATE: Check out Zephyr for BL602</strong></a></p>
<p><a href="https://github.com/bouffalolab/bl_mcu_sdk/pull/18"><strong>UPDATE: Zephyr is being ported to BL602 MCU SDK</strong></a></p>
<h1 id="adapt-from-existing-risc-v-port"><a class="doc-anchor" href="#adapt-from-existing-risc-v-port">§</a>1 Adapt from Existing RISC-V Port</h1>
<p><em>Whats the quickest way to port Mynewt to PineCone BL602?</em></p>
<p>Theres one (and only one) RISC-V Board supported today on Mynewt: <strong>SiFives HiFive1 Board</strong>, based on the <strong>SiFive FE310 Microcontroller</strong>.</p>
<p>We shall copy and adapt the necessary files from the HiFive1 FE310 port to our PineCone BL602 port.</p>
<p><em>How different is BL602 from SiFive FE310?</em></p>
<p>The Memory Maps for BL602 and SiFive FE310 look totally different…</p>
<p><img src="https://lupyuen.github.io/images/pinecone-compare.jpg" alt="BL602 Memory Map vs SiFive FE310: Totally different" /></p>
<p><em>BL602 Memory Map (left) vs SiFive FE310 (right): Totally different</em></p>
<p>But <strong>BL602s RISC-V Core is highly similar to SiFive FE310</strong>. Compare these two files…</p>
<ol>
<li>
<p><a href="https://github.com/pine64/bl_iot_sdk/blob/master/components/bl602/freertos_riscv/config/platform.h"><code>platform.h</code> from <strong>BL602 IoT SDK</strong></a></p>
</li>
<li>
<p><a href="https://github.com/apache/mynewt-core/blob/master/hw/mcu/sifive/src/ext/freedom-e-sdk_3235929/bsp/env/freedom-e300-hifive1/platform.h"><code>platform.h</code> from <strong>Mynewts FE310 Port</strong></a></p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/mynewt-platform.png" alt="platform.h: BL602 (left) vs SiFive FE310 (right)" /></p>
<p><em>platform.h: BL602 (left) vs SiFive FE310 (right)</em></p>
<p>Since BL602s RISC-V Core is so similar to FE310, it makes porting simpler.</p>
<p><img src="https://lupyuen.github.io/images/mynewt-e21.png" alt="BL602 is based on SiFive E21 RISC-V Core" /></p>
<p><em>BL602 is based on which SiFive RISC-V Core?</em></p>
<p>From the screenshot above, the name “E21” appears (over a hundred times) in the BL602 IoT SDK. <a href="https://github.com/pine64/bl_iot_sdk/blob/master/components/bl602/bl602_std/bl602_std/Device/Bouffalo/BL602/Peripherals/l1c_reg.h#L178-L194">(See this)</a></p>
<p>Thus we assume that BL602 is based on the <strong>SiFive E21 RISC-V Core</strong> (and not E24)…</p>
<ul>
<li><a href="https://sifive.cdn.prismic.io/sifive/39d336f7-7dba-43f2-a453-8d55227976cc_sifive_E21_rtl_full_20G1.03.00_manual.pdf">SiFive E21 Manual</a></li>
</ul>
<p>While doing the porting, we shall compare the above E21 doc with the FE310 doc so that we can identify the differences (e.g. FE310 supports PLIC, E21 doesnt)</p>
<ul>
<li><a href="https://sifive.cdn.prismic.io/sifive/4d063bf8-3ae6-4db6-9843-ee9076ebadf7_fe310-g000.pdf">SiFive FE310 Manual</a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/mynewt-gcc.png" alt="Mynewts default GCC Compiler is riscv64-unknown-elf-gcc" /></p>
<p><em>Mynewts default GCC Compiler is <code>riscv64-unknown-elf-gcc</code></em></p>
<h1 id="set-gcc-compiler-for-risc-v"><a class="doc-anchor" href="#set-gcc-compiler-for-risc-v">§</a>2 Set GCC Compiler for RISC-V</h1>
<p>When building RISC-V Firmware, Mynewt uses the RISC-V GCC Compiler <code>riscv64-unknown-elf-gcc</code> <a href="https://github.com/apache/mynewt-core/blob/master/compiler/riscv64/compiler.yml">(See this)</a></p>
<p>But thats not the same as our compiler from xPack RISC-V GCC: <code>riscv-none-embed-gcc</code></p>
<p><em>(See <a href="https://lupyuen.github.io/articles/debug">“Debug Rust on PineCone BL602 with VSCode and GDB”</a>, Section 1.3, <a href="https://lupyuen.github.io/articles/debug#install-gdb">“Install GDB”</a>)</em></p>
<p>Hence we copy and modify the GCC settings like so: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/compiler/riscv-none-embed/compiler.yml"><code>compiler/riscv-none-embed/compiler.yml</code></a></p>
<div class="example-wrap"><pre class="language-yaml"><code>compiler.path.cc: &quot;riscv-none-embed-gcc&quot;
compiler.path.as: &quot;riscv-none-embed-gcc&quot;
compiler.path.archive: &quot;riscv-none-embed-ar&quot;
compiler.path.objdump: &quot;riscv-none-embed-objdump&quot;
compiler.path.objsize: &quot;riscv-none-embed-size&quot;
compiler.path.objcopy: &quot;riscv-none-embed-objcopy&quot;</code></pre></div>
<p>Mynewt will now compile our firmware with <code>riscv-none-embed-gcc</code></p>
<h2 id="mynewt-project-and-firmware"><a class="doc-anchor" href="#mynewt-project-and-firmware">§</a>2.1 Mynewt Project and Firmware</h2>
<p><em>In the screen above, how did we create the Mynewt Project <code>pinecone-rust-mynewt</code> and the Mynewt Firmware <code>pinecone_app</code>?</em></p>
<p>I created <code>pinecone-rust-mynewt</code> and <code>pinecone_app</code> using Mynewts <code>newt</code> tool.</p>
<p>Well download them in a while, so you dont need to create them.</p>
<p><em>(FYI: I created <code>pinecone-rust-mynewt</code> and <code>pinecone_app</code> using the steps explained in the sections “Appendix: Install newt” and “Appendix: Create the Mynewt Firmware” below)</em></p>
<p><img src="https://lupyuen.github.io/images/mynewt-mcu.png" alt="Mynewt Microcontroller Definition for BL602" /></p>
<p><em>Mynewt Microcontroller Definition for BL602</em></p>
<h1 id="add-microcontroller-definition"><a class="doc-anchor" href="#add-microcontroller-definition">§</a>3 Add Microcontroller Definition</h1>
<p>We create a <strong>Microcontroller Definition</strong> to tell Mynewt all about BL602…</p>
<ul>
<li>
<p><strong>BL602 Microcontroller Definition</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/mcu/bl/bl602"><code>hw/mcu/bl/bl602</code></a></p>
</li>
<li>
<p><strong>BL602 Package</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/pkg.yml"><code>pkg.yml</code></a></p>
</li>
<li>
<p><strong>BL602 Configuration</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/syscfg.yml"><code>syscfg.yml</code></a></p>
</li>
</ul>
<p>This contains the code for the <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/mcu/bl/bl602/src"><strong>Hardware Adaptaion Layer</strong></a> thats specific to BL602 and its built-in Periperal Functions (like Flash Memory, GPIO, I2C, SPI, …)</p>
<p>The code here was derived from SiFive FE310: <a href="https://github.com/apache/mynewt-core/tree/master/hw/mcu/sifive/fe310"><code>hw/mcu/sifive/fe310</code></a></p>
<h1 id="add-board-support-package"><a class="doc-anchor" href="#add-board-support-package">§</a>4 Add Board Support Package</h1>
<p>BL602 is present on various boards, PineCone is one of them. The BL602 boards have different features: LEDs, buttons, JTAG debugger, …</p>
<p>In Mynewt we handle the board differences by creating a <strong>Board Support Package</strong> for PineCone…</p>
<ul>
<li>
<p><strong>PineCone Board Support Package</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/bsp/pinecone"><code>hw/bsp/pinecone</code></a></p>
</li>
<li>
<p><strong>PineCone Definition</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/bsp.yml"><code>bsp.yml</code></a></p>
</li>
<li>
<p><strong>PineCone Package</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/pkg.yml"><code>pkg.yml</code></a></p>
</li>
<li>
<p><strong>PineCone Configuration</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/syscfg.yml"><code>syscfg.yml</code></a></p>
</li>
</ul>
<p>The Board Support Package for PineCone contains code thats specific to PineCone. <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/bsp/pinecone/src">More details</a></p>
<p>The code here was derived from SiFive HiFive1 Board: <a href="https://github.com/apache/mynewt-core/tree/master/hw/bsp/hifive1"><code>hw/bsp/hifive1</code></a></p>
<h1 id="define-linker-script"><a class="doc-anchor" href="#define-linker-script">§</a>5 Define Linker Script</h1>
<p>The Linker Script tells GCC Compiler about the Memory Layout for executing our firmware…</p>
<ol>
<li>
<p><strong>Flash Memory Area</strong>: For firmware code and read-only data</p>
</li>
<li>
<p><strong>RAM Memory Area</strong>: For read/write data</p>
</li>
</ol>
<p>Heres our Linker Script for PineCone…</p>
<ul>
<li><strong>PineCone Linker Script</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/bsp_app.ld"><code>hw/bsp/pinecone/bsp_app.ld</code></a></li>
</ul>
<div class="example-wrap"><pre class="language-text"><code>MEMORY
{
/* Use this memory layout when firmware is loaded into cache memory.
Based on https://github.com/lupyuen/pinecone-rust/blob/main/memory.x */
flash (rxai!w) : ORIGIN = 0x22008000, LENGTH = 48K /* Instruction Cache Memory */
ram (wxa!ri) : ORIGIN = 0x22014000, LENGTH = 48K /* Data Cache Memory */
}</code></pre></div>
<p>Note that were loading the firmware code and read-only data into BL602s Instruction Cache Memory (similar to RAM), not into Flash Memory. (Well learn why in a while)</p>
<p>In future when were ready to load our firmware into Flash Memory, well use this memory layout instead…</p>
<div class="example-wrap"><pre class="language-text"><code> /* TODO: Use this memory layout when firmware is loaded into Flash Memory
Based on Based on https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602/evb/ld/flash_rom.ld */
flash (rxai!w) : ORIGIN = 0x23000000, LENGTH = 4M /* Flash Memory */
ram (wxa!ri) : ORIGIN = 0x4200c000, LENGTH = 216K /* RAM */</code></pre></div>
<p>(This is commented out in <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/bsp_app.ld"><code>bsp_app.ld</code></a>)</p>
<h2 id="bootloader-image-header"><a class="doc-anchor" href="#bootloader-image-header">§</a>5.1 Bootloader Image Header</h2>
<p>Were presently not using a Bootloader on PineCone…</p>
<div class="example-wrap"><pre class="language-text"><code>/* Bootloader not in use. */
_imghdr_size = 0x0;</code></pre></div>
<p>In future when we use the Mynewt Bootloader, we need to reserve some space for the Bootloader Image Header, which is located at the start of the firmware code…</p>
<div class="example-wrap"><pre class="language-text"><code>/* This linker script is used for images and thus contains an image header */
/* TODO: Uncomment the next line when Bootloader is in use */
_imghdr_size = 0x20;</code></pre></div><h1 id="define-flash-map"><a class="doc-anchor" href="#define-flash-map">§</a>6 Define Flash Map</h1>
<p>Mynewts MCUBoot Bootloader will roll back the Active Firmware to the Standby Firmware in case the Active Firmware cant be started.</p>
<p>We define the <strong>Flash Map</strong> to tell Mynewt where in Flash Memory the Bootloader, Active Firmware Image and Standby Firmware Image will be located…</p>
<ul>
<li><strong>PineCone Flash Map</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/bsp.yml"><code>hw/bsp/pinecone/bsp.yml</code></a></li>
</ul>
<div class="example-wrap"><pre class="language-yaml"><code>## BL602 Instruction Cache Memory starts at 0x2200 8000, size 48 KB
## Based on https://github.com/lupyuen/pinecone-rust/blob/main/memory.x
bsp.flash_map:
areas:
## System areas.
## (Not Used) Bootloader
FLASH_AREA_BOOTLOADER:
device: 0
offset: 0x22013c00
size: 1kB # 0x400
## Active Firmware Image
FLASH_AREA_IMAGE_0:
device: 0
offset: 0x22008000
size: 43kB # 0xac00
## (Not Used) Standby Firmware Image, in case Active Firmware can&#39;t start
FLASH_AREA_IMAGE_1:
device: 0
offset: 0x22012c00
size: 1kB # 0x400
## (Not used) Scratch Area for swapping Active Firmware and Standby Firmware
FLASH_AREA_IMAGE_SCRATCH:
device: 0
offset: 0x22013000
size: 1kB # 0x400</code></pre></div>
<p>Remember that were loading our firmware into Cache Memory (instead of Flash Memory) and were not using the Bootloader.</p>
<p>Thats why we allocate most of the Cache Memory to the Active Firmware Image (located at the start of Cache Memory).</p>
<div class="example-wrap"><pre class="language-yaml"><code> ## User areas.
## (Not Used) Reboot Log
FLASH_AREA_REBOOT_LOG:
user_id: 0
device: 0
offset: 0x22013400
size: 1kB # 0x400
## (Not Used) User File System, like LittleFS
FLASH_AREA_NFFS:
user_id: 1
device: 0
offset: 0x22013800
size: 1kB # 0x400</code></pre></div>
<p>Since we have very little Cache Memory, well cut down on the Reboot Log and User File Systems.</p>
<h2 id="future-flash-map"><a class="doc-anchor" href="#future-flash-map">§</a>6.1 Future Flash Map</h2>
<p>The Flash Map looks more meaningful when were ready to load our firmware into Flash Memory and turn on the Bootloader.</p>
<p>Here is our Flash Map for the future…</p>
<div class="example-wrap"><pre class="language-yaml"><code>## TODO: Use this memory layout when firmware is loaded into Flash Memory
## BL602 Flash starts at 0x2300 0000, size 4 MB
## Based on https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602/evb/ld/flash_rom.ld
bsp.flash_map:
areas:
## System areas.
## TODO: Bootloader not in use. When used, move Bootloader to 0x2300 0000 and shift the other areas accordingly
FLASH_AREA_BOOTLOADER:
device: 0
offset: 0x2330d000
size: 32kB # 0x8000
## Active Firmware Image
FLASH_AREA_IMAGE_0:
device: 0
offset: 0x23000000
size: 1024kB # 0x100 000
## Standby Firmware Image, in case Active Firmware can&#39;t start
FLASH_AREA_IMAGE_1:
device: 0
offset: 0x23100000
size: 1024kB # 0x100 000
## Scratch Area for swapping Active Firmware and Standby Firmware
FLASH_AREA_IMAGE_SCRATCH:
device: 0
offset: 0x23300000
size: 4kB # 0x1000</code></pre></div>
<p>(This is commented out in <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/bsp/pinecone/bsp.yml"><code>bsp.yml</code></a>)</p>
<p>In future well have a proper Reboot Log and a User File System for saving files and data that will be retained across reboots…</p>
<div class="example-wrap"><pre class="language-yaml"><code> ## User areas.
## Reboot Log
FLASH_AREA_REBOOT_LOG:
user_id: 0
device: 0
offset: 0x23301000
size: 48kB # 0xc000
## User File System, like LittleFS
FLASH_AREA_NFFS:
user_id: 1
device: 0
offset: 0x23200000
size: 1024kB # 0x100 000</code></pre></div><h1 id="set-firmware-target"><a class="doc-anchor" href="#set-firmware-target">§</a>7 Set Firmware Target</h1>
<p>We select the Mynewt Firmware to be built by creating a Firmware Target…</p>
<ul>
<li><strong>PineCone Firmware Target</strong>: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/targets/pinecone_app/target.yml"><code>targets/pinecone_app/target.yml</code></a></li>
</ul>
<div class="example-wrap"><pre class="language-yaml"><code>target.app: apps/blinky
target.bsp: &quot;hw/bsp/pinecone&quot;
target.build_profile: debug</code></pre></div>
<p>Here we specify that our firmware code comes from the <a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/apps/blinky">Blinky Sample App</a>. And our firmware will be compiled for the PineCone BL602 Board.</p>
<p>Also check out the <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/targets/pinecone_app/pkg.yml"><strong>Target Package</strong></a> and the <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/targets/pinecone_app/syscfg.yml"><strong>Target Configuration</strong></a>.</p>
<h1 id="build-the-firmware"><a class="doc-anchor" href="#build-the-firmware">§</a>8 Build the Firmware</h1>
<p>We have created a minimal port of Mynewt to PineCone. Heres how we build the firmware on Linux, macOS and Windows (plain old CMD, without WSL and MSYS2)…</p>
<ol>
<li>
<p>Install Mynewts <code>newt</code> tool according to the instructions here…</p>
<ul>
<li><a href="https://mynewt.apache.org/latest/newt/install/index.html">Installing <code>newt</code></a></li>
</ul>
<p>To build <code>newt</code> from the source code, check the section “Appendix: Install newt” below</p>
</li>
<li>
<p>At the command prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Download source files
git clone --recursive https://github.com/lupyuen/pinecone-rust-mynewt
cd pinecone-rust-mynewt</code></pre></div></li>
<li>
<p>Download GCC from the <a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/tag/v8.3.0-2.3">xPack GCC for RISC-V site</a></p>
<ul>
<li>
<p><a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.3.0-2.3/xpack-riscv-none-embed-gcc-8.3.0-2.3-linux-x64.tar.gz">xPack GCC RISC-V for Linux x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.3.0-2.3/xpack-riscv-none-embed-gcc-8.3.0-2.3-linux-arm64.tar.gz">xPack GCC RISC-V for Linux Arm64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.3.0-2.3/xpack-riscv-none-embed-gcc-8.3.0-2.3-darwin-x64.tar.gz">xPack GCC RISC-V for macOS x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v8.3.0-2.3/xpack-riscv-none-embed-gcc-8.3.0-2.3-win32-x64.zip">xPack GCC RISC-V for Windows x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/tag/v8.3.0-2.3">Other builds of xPack GCC RISC-V</a></p>
</li>
</ul>
<p>Extract the downloaded archive.</p>
</li>
<li>
<p>Copy the extracted xPack GCC RISC-V folder to the <code>pinecone-rust-mynewt</code> folder.</p>
<p>Rename the copied folder as…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/xpack-riscv-none-embed-gcc</code></pre></div>
<p><strong>For Windows:</strong> Add the full path of <code>xpack-riscv-none-embed-gcc/bin</code> to the PATH. For example…</p>
<div class="example-wrap"><pre class="language-text"><code>c:\pinecone-rust-mynewt\xpack-riscv-none-embed-gcc\bin</code></pre></div></li>
<li>
<p>Download OpenOCD from the <a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/tag/v0.10.0-15/">xPack OpenOCD site</a>… (Other variants of OpenOCD may not work with PineCone)</p>
<ul>
<li>
<p><a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.10.0-15/xpack-openocd-0.10.0-15-linux-x64.tar.gz">xPack OpenOCD for Linux x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.10.0-15/xpack-openocd-0.10.0-15-linux-arm64.tar.gz">xPack OpenOCD for Linux Arm64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.10.0-15/xpack-openocd-0.10.0-15-darwin-x64.tar.gz">xPack OpenOCD for macOS x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v0.10.0-15/xpack-openocd-0.10.0-15-win32-x64.zip">xPack OpenOCD for Windows x64</a></p>
</li>
<li>
<p><a href="https://github.com/xpack-dev-tools/openocd-xpack/releases/tag/v0.10.0-15/">Other builds of xPack OpenOCD</a></p>
</li>
</ul>
<p>Extract the downloaded archive.</p>
</li>
<li>
<p>Copy the extracted xPack OpenOCD folder to the <code>pinecone-rust-mynewt</code> folder.</p>
<p>Rename the copied folder as…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/xpack-openocd</code></pre></div>
<p><strong>For Windows:</strong> Add the full path of <code>xpack-openocd/bin</code> to the PATH. For example…</p>
<div class="example-wrap"><pre class="language-text"><code>c:\pinecone-rust-mynewt\pinecone-rust-mynewt\xpack-openocd\bin</code></pre></div></li>
<li>
<p><strong>For Linux and macOS:</strong> Enter at the command prompt…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Build the firmware
export PATH=&quot;$PWD/xpack-riscv-none-embed-gcc/bin:$PATH&quot;
newt build pinecone_app
## Display the firmware size
newt size -v pinecone_app</code></pre></div>
<p><strong>For Windows:</strong> Enter at the command prompt…</p>
<div class="example-wrap"><pre class="language-cmd"><code>:: Build the firmware
newt\newt.exe build pinecone_app
:: Display the firmware size
newt\newt.exe size -v pinecone_app</code></pre></div></li>
</ol>
<p>We should see this…</p>
<div class="example-wrap"><pre class="language-text"><code>Linking /Users/Luppy/pinecone/pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf
Target successfully built: targets/pinecone_app</code></pre></div>
<p>Followed by the size of the firmware (8,488 bytes) and its library components…</p>
<div class="example-wrap"><pre class="language-text"><code>+ newt size -v pinecone_app
Size of Application Image: app
Mem flash: 0x22008000-0x22014000
Mem ram: 0x22014000-0x22020000
flash ram
6 525 *fill*
172 0 @apache-mynewt-core_hw_hal.a
4494 8213 @apache-mynewt-core_kernel_os.a
80 0 @apache-mynewt-core_libc_baselibc.a
702 128 @apache-mynewt-core_sys_flash_map.a
2 0 @apache-mynewt-core_sys_log_modlog.a
782 29 @apache-mynewt-core_sys_mfg.a
30 5 @apache-mynewt-core_sys_sysinit.a
72 0 @apache-mynewt-core_util_mem.a
60 8 apps_blinky.a
44 12 hw_bsp_pinecone.a
580 228 hw_mcu_bl_bl602.a
92 0 pinecone_app-sysinit-app.a
292 1064 libg.a
Loading compiler pinecone-rust-mynewt/compiler/riscv-none-embed, buildProfile debug
objsize
text data bss dec hex filename
8488 28 9104 17620 44d4 pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf</code></pre></div>
<p>The compiled ELF firmware is located at…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf</code></pre></div><h1 id="implement-hardware-abstraction-layer"><a class="doc-anchor" href="#implement-hardware-abstraction-layer">§</a>9 Implement Hardware Abstraction Layer</h1>
<p>The above steps will build successfully a minimal port of Mynewt for PineCone.</p>
<p>Thats because I have fixed many missing functions in Mynewts Hardware Abstraction Layer (HAL), like these…</p>
<p><img src="https://lupyuen.github.io/images/mynewt-hal.png" alt="Missing Functions in Mynewt HAL" /></p>
<p><em>Missing Functions in Mynewt HAL</em></p>
<p>We can see that Mynewts HAL consists of low-level functions that control BL602s hardware functions: Flash Memory, Interrupts, Watchdog, GPIO, …</p>
<p>Well be filling in these missing HAL functions someday… But for now I have inserted Stub Functions.</p>
<p>Which means that the firmware will build OK… Just that GPIO and other features wont actually work when we run the firmware.</p>
<p><em>How shall we fill in the HAL Functions for PineCone?</em></p>
<p>The BL602 HAL functions (GPIO, I2C, SPI, …) are already implemented here…</p>
<ul>
<li><a href="https://github.com/lupyuen/bl_iot_sdk/tree/master/components"><strong>BL602 IoT SDK Firmware Components</strong></a></li>
</ul>
<p>We shall copy the source files from above and embed them here…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/mcu/bl/bl602/ext"><strong>Mynewt External Source Files for BL602</strong></a></li>
</ul>
<p>The BL602 SDK Functions look different from the Mynewt HAL API. Thus well have to create some adapter code in C to make the BL602 Functions look like the Mynewt HAL.</p>
<p>The code that adapts the BL602 SDK to Mynewt HAL shall be placed here…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt/tree/main/hw/mcu/bl/bl602/src"><strong>Mynewt HAL for BL602</strong></a></li>
</ul>
<p>As we can see from the GPIO pic below, our job now is to <strong>adapt the BL602 SDK</strong> (left) <strong>to the Mynewt HAL</strong> (right).</p>
<p>(For reference: Heres how the <a href="https://github.com/apache/mynewt-core/tree/master/hw/mcu/sifive/fe310/src">Mynewt HAL for SiFive FE310</a> is adapted from the <a href="https://github.com/apache/mynewt-core/tree/master/hw/mcu/sifive/src/ext/freedom-e-sdk_3235929">FE310 SDK</a>)</p>
<p><img src="https://lupyuen.github.io/images/mynewt-hal2.png" alt="BL602 GPIO SDK (left) vs Mynewt GPIO HAL (right)" /></p>
<p><em>BL602 GPIO SDK (left) vs Mynewt GPIO HAL (right)</em></p>
<h1 id="implement-start-code"><a class="doc-anchor" href="#implement-start-code">§</a>10 Implement Start Code</h1>
<p>Most firmware will have some Start Code (written in Assembly Code) that will be executed when the firmware starts.</p>
<p>For the BL602 IoT SDK, this is the Start Code (in RISC-V Assembly)…</p>
<ul>
<li><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602/evb/src/boot/gcc/start.S"><strong>Start Code from BL602 IoT SDK: <code>start.S</code></strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/mynewt-start.png" alt="Start Code from BL602 IoT SDK: start.S" /></p>
<p><em>Start Code from BL602 IoT SDK: start.S</em></p>
<p>For Mynewt were using this Start Code instead…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/src/arch/rv32imac/start.s"><strong>Start Code for Mynewt BL602: <code>start.s</code></strong></a></li>
</ul>
<p>(Adapted from <a href="https://github.com/apache/mynewt-core/blob/master/hw/mcu/sifive/fe310/src/arch/rv32imac/start.s">FE310 Start Code</a>)</p>
<p>Mynewts Start Code initialises the RAM before calling the <code>main</code> function.</p>
<p><em>Is Mynewts Start Code any different from the BL602 SDK?</em></p>
<p>When we compare Mynewts Start Code with the BL602 SDK, we see that the BL602 SDK Start Code uses the Boot Partition and Flash Configuration. <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602/evb/src/boot/gcc/start.S#L27-L54">More details</a></p>
<p>This code will have to be inserted into Mynewts Start Code, when our firmware is ready to be loaded into Flash Memory.</p>
<h1 id="risc-v-rv32imfc-vs-rv32imac"><a class="doc-anchor" href="#risc-v-rv32imfc-vs-rv32imac">§</a>11 RISC-V rv32imfc vs rv32imac</h1>
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/make_scripts_riscv/project.mk#L223">According to the SDK</a>, BL602 uses a RISC-V Core (SiFive E21) thats designated <strong><code>rv32imfc</code></strong> based on its capabilities…</p>
<div><table><thead><tr><th style="text-align: center">Designation</th><th style="text-align: left">Meaning</th></tr></thead><tbody>
<tr><td style="text-align: center"><strong><code>rv32i</code></strong></td><td style="text-align: left">32-bit RISC-V with Base Integer Instructions</td></tr>
<tr><td style="text-align: center"><strong><code>m</code></strong></td><td style="text-align: left">Integer Multiplication + Division</td></tr>
<tr><td style="text-align: center"><strong><code>f</code></strong></td><td style="text-align: left"><strong>Single-Precision Hardware Floating Point</strong></td></tr>
<tr><td style="text-align: center"><strong><code>c</code></strong></td><td style="text-align: left">Compressed Instructions</td></tr>
</tbody></table>
</div>
<p><a href="https://en.wikipedia.org/wiki/RISC-V#ISA_base_and_extensions">(Heres the whole list)</a></p>
<p><strong>UPDATE:</strong> BL602 actually supports <strong><code>rv32acfimx</code></strong> <a href="https://lupyuen.github.io/articles/uart#enter-the-commands">(See this)</a></p>
<p>However Mynewt today supports only <strong><code>rv32imac</code></strong></p>
<div><table><thead><tr><th style="text-align: center">Designation</th><th style="text-align: left">Meaning</th></tr></thead><tbody>
<tr><td style="text-align: center"><strong><code>rv32i</code></strong></td><td style="text-align: left">32-bit RISC-V with Base Integer Instructions</td></tr>
<tr><td style="text-align: center"><strong><code>m</code></strong></td><td style="text-align: left">Integer Multiplication + Division</td></tr>
<tr><td style="text-align: center"><strong><code>a</code></strong></td><td style="text-align: left"><strong>Atomic Instructions</strong></td></tr>
<tr><td style="text-align: center"><strong><code>c</code></strong></td><td style="text-align: left">Compressed Instructions</td></tr>
</tbody></table>
</div>
<p><em>Whats the difference?</em></p>
<p>Mynewt doesnt support RISC-V <strong>Hardware Floating Point</strong> yet… But it supports <strong>Atomic Instructions</strong> (for data synchronisation).</p>
<p>Thus for now well compile our Mynewt Firmware for <code>rv32imac</code> (without Hardware Floating Point)…</p>
<ul>
<li><a href="https://github.com/apache/mynewt-core/tree/master/kernel/os/src/arch/rv32imac"><strong>Mynewt Support for <code>rv32imac</code></strong></a></li>
</ul>
<p>In future well have to implement <code>rv32imfc</code> (with Hardware Floating Point) in Mynewt.</p>
<p><img src="https://lupyuen.github.io/images/mynewt-fe310.png" alt="SiFive FE310 Reference in Mynewt rv32imac" /></p>
<p><em>SiFive FE310 Reference in Mynewt rv32imac</em></p>
<h1 id="decouple-sifive-fe310-from-rv32imac"><a class="doc-anchor" href="#decouple-sifive-fe310-from-rv32imac">§</a>12 Decouple SiFive FE310 from rv32imac</h1>
<p>Theres a peculiar problem compiling RISC-V Firmware on Mynewt…</p>
<div class="example-wrap"><pre class="language-text"><code>Error: In file included from ...
repos/apache-mynewt-core/kernel/os/include/os/arch/rv32imac/os/os_arch.h:24:10:
fatal error: mcu/fe310.h: No such file or directory
#include &quot;mcu/fe310.h&quot;</code></pre></div>
<p>This error shows that <code>rv32imac</code>, the RISC-V support in Mynewt, is dependent on SiFive FE310. Which looks really odd.</p>
<p>(Probably done that way because FE310 is the only RISC-V Microcontroller supported by Mynewt)</p>
<p>We work around this problem by creating Stub Files like these…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/include/mcu/fe310.h"><code>mcu/fe310.h</code></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/include/env/freedom-e300-hifive1/platform.h"><code>env/freedom-e300-hifive1/platform.h</code></a></p>
</li>
</ul>
<p>These Stub Files point to the correct Header Files for BL602, so that our BL602 Firmware can be compiled successfully.</p>
<h1 id="inspect-the-firmware"><a class="doc-anchor" href="#inspect-the-firmware">§</a>13 Inspect the Firmware</h1>
<p>Were almost ready to run Mynewt on PineCone! Lets do one final check before running our firmware…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Build the firmware
export PATH=&quot;$PWD/xpack-riscv-none-embed-gcc/bin:$PATH&quot;
newt build pinecone_app
## Display the firmware size
newt size -v pinecone_app</code></pre></div>
<p>We should see…</p>
<div class="example-wrap"><pre class="language-text"><code>Linking pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf
Target successfully built: targets/pinecone_app
+ newt size -v pinecone_app
Size of Application Image: app
Mem flash: 0x22008000-0x22014000
Mem ram: 0x22014000-0x22020000</code></pre></div>
<p>Yep this matches our Instruction Cache Memory (<code>0x2200 8000</code>) and Data Cache Memory (<code>0x2201 4000</code>).</p>
<div class="example-wrap"><pre class="language-text"><code> flash ram
6 525 *fill*
172 0 @apache-mynewt-core_hw_hal.a
4494 8213 @apache-mynewt-core_kernel_os.a
80 0 @apache-mynewt-core_libc_baselibc.a
702 128 @apache-mynewt-core_sys_flash_map.a
2 0 @apache-mynewt-core_sys_log_modlog.a
782 29 @apache-mynewt-core_sys_mfg.a
30 5 @apache-mynewt-core_sys_sysinit.a
72 0 @apache-mynewt-core_util_mem.a
60 8 apps_blinky.a
44 12 hw_bsp_pinecone.a
580 228 hw_mcu_bl_bl602.a
92 0 pinecone_app-sysinit-app.a
292 1064 libg.a</code></pre></div>
<p>Here are all the code modules linked into our Mynewt Firmware. Note that…</p>
<ul>
<li>
<p>Mynewt Kernel takes the most memory</p>
</li>
<li>
<p>Our BL602 HAL <code>hw_mcu_bl_bl602</code> is tiny because its mostly Stub Functions</p>
</li>
</ul>
<div class="example-wrap"><pre class="language-text"><code>Loading compiler pinecone-rust-mynewt/compiler/riscv-none-embed, buildProfile debug
objsize
text data bss dec hex filename
8488 28 9104 17620 44d4 pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf</code></pre></div>
<p>Our Mynewt Firmware contains 8,488 bytes of code and data. It runs with 9,104 bytes of RAM (BSS).</p>
<p>The firmware build produces the following files in…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky</code></pre></div>
<ul>
<li>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf"><strong><code>blinky.elf</code></strong></a>: Our Mynewt Firmware in ELF Format (<a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf">See this</a>)</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf.map"><strong><code>blinky.elf.map</code></strong></a>: Memory Map of our Mynewt Firmware (<a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf.map">See this</a>)</p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf.lst"><strong><code>blinky.elf.lst</code></strong></a>: RISC-V Disassembly of our Mynewt Firmware (<a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf.lst">See this</a>)</p>
</li>
</ul>
<p><img src="https://lupyuen.github.io/images/mynewt-disassembly.png" alt="RISC-V Disassembly of Mynewt Firmware" /></p>
<p><em>RISC-V Disassembly of Mynewt Firmware</em></p>
<p>Inspect the RISC-V Disassembly: <a href="https://github.com/lupyuen/pinecone-rust-mynewt/releases/download/v1.0.0/blinky.elf.lst"><code>blinky.elf.lst</code></a></p>
<p>It should look similar to our <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/hw/mcu/bl/bl602/src/arch/rv32imac/start.s">Start Code</a>. And it should be located at the Start Address of our firmware: <code>0x2200 8000</code>.</p>
<p>Were ready to run our Mynewt Firmware on PineCone!</p>
<h1 id="debug-firmware-with-vscode"><a class="doc-anchor" href="#debug-firmware-with-vscode">§</a>14 Debug Firmware with VSCode</h1>
<p>Now we run and debug our Mynewt Firmware with <a href="https://code.visualstudio.com/"><strong>VSCode</strong></a> on Linux, macOS and Windows…</p>
<ol>
<li>
<p>Connect PineCone and the JTAG Debugger to our computer. See the article…</p>
<p><a href="https://lupyuen.github.io/articles/openocd">“Connect PineCone BL602 to OpenOCD”</a>, Section 4, <a href="https://lupyuen.github.io/articles/openocd#connect-jtag-debugger-to-pinecone">“Connect JTAG Debugger to PineCone”</a></p>
</li>
<li>
<p>Launch VSCode</p>
</li>
<li>
<p>Click <strong><code>File → Open</code></strong></p>
<p>Select the folder <strong><code>pinecone-rust-mynewt</code></strong></p>
</li>
<li>
<p>Click <strong><code>Terminal → Run Build Task</code></strong></p>
<p>This builds the Mynewt Firmware. The RISC-V ELF Firmware image is generated here…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/bin/targets/pinecone_app/app/apps/blinky/blinky.elf</code></pre></div>
<p>This step also terminates any OpenOCD processes that are running. (Linux and macOS only)</p>
</li>
<li>
<p>Click <strong><code>Run → Start Debugging</code></strong></p>
<p>The debugger loads our Mynewt Firmware to PineCones Cache Memory and begins execution.</p>
<p>Click <strong><code>View → Debug Console</code></strong> to view the Debug Console. GDB messages will be shown here.</p>
</li>
<li>
<p>The debugger pauses execution at the first line of the <code>main</code> function</p>
<p>We should see the screen below…</p>
<p><a href="https://youtu.be/iDS8CBplSw8">Watch on YouTube</a></p>
</li>
</ol>
<p><img src="https://lupyuen.github.io/images/mynewt-debug.png" alt="Debug Firmware with VSCode" /></p>
<p><em>Debug Firmware with VSCode</em></p>
<h2 id="debugging-features"><a class="doc-anchor" href="#debugging-features">§</a>14.1 Debugging Features</h2>
<p>We may use these features for debugging our Mynewt Firmware…</p>
<ol>
<li>
<p><strong>Variables</strong> (Left Top Pane): Inspect global and local variables</p>
</li>
<li>
<p><strong>Watch</strong> (Left Centre): Show the value of expressions</p>
</li>
<li>
<p><strong>Call Stack</strong> (Left Bottom): Navigate the stack trace and its variables</p>
</li>
<li>
<p><strong>Debug Console</strong> (Centre): Enter GDB commands here</p>
</li>
<li>
<p><strong>Debug Toolbar</strong> (Top Right): Continue / Pause, Step Over, Step Into, Step Out, Restart, Stop</p>
</li>
<li>
<p>To set a <strong>Breakpoint</strong>, click the Gutter Column at the left of the source code</p>
</li>
<li>
<p>When were done with debugging, click the Stop button in the Debug Toolbar at top right</p>
</li>
</ol>
<p><a href="https://youtu.be/iDS8CBplSw8">Watch on YouTube</a></p>
<p><a href="https://code.visualstudio.com/docs/editor/debugging">More about VSCode Debugger</a></p>
<h2 id="terminating-openocd"><a class="doc-anchor" href="#terminating-openocd">§</a>14.2 Terminating OpenOCD</h2>
<p>Before we start a new debugging session with <strong><code>Run → Start Debugging</code></strong></p>
<p><em>We must always click <strong><code>Terminal → Run Build Task</code></strong> first!</em></p>
<p>Thats because stopping the debugger will leave OpenOCD running (and locking up the connection to PineCone).</p>
<p>Clicking <strong><code>Run Build Task</code></strong> will terminate the OpenOCD task, so that the next debugging session can restart OpenOCD successfully.</p>
<p>For Windows: Sorry we need to terminate the OpenOCD task manually with the Task Manager.</p>
<p>In case of OpenOCD problems, check the OpenOCD log file…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt/openocd.log</code></pre></div>
<p>For details on the VSCode settings, check the section “Appendix: VSCode Settings” below.</p>
<h1 id="how-to-test"><a class="doc-anchor" href="#how-to-test">§</a>15 How To Test</h1>
<p><em>How shall we test Mynewt on PineCone? Or any other RTOS ported to PineCone?</em></p>
<p>We have an interesting problem here… PineCone is a barebones board that doesnt have any sensors or actuators connected on interfaces like I2C and SPI.</p>
<p>It will be challenging to test the various interfaces ported to Mynewt. (I might test with the <a href="http://dangerousprototypes.com/docs/Bus_Pirate"><strong>Bus Pirate Probe</strong></a>)</p>
<p>For now Ill do <strong>“Opportunistic Porting and Testing”</strong>… Ill port to Mynewt only those PineCone Interfaces that I can test.</p>
<p><strong>Do you have ideas for testing an RTOS on PineCone? Let us know!</strong></p>
<h2 id="testing-the-led"><a class="doc-anchor" href="#testing-the-led">§</a>15.1 Testing the LED</h2>
<p>Testing PineCones onboard RGB LED over GPIO seems easy… Except that the LED is connected to the JTAG Port. So the debugger will fail.</p>
<p>In the earlier articles we learnt about remapping the JTAG port. This could be a (complicated) solution to test and debug the GPIO Port.</p>
<p>Meanwhile Ill proceed to port the GPIO HAL from the BL602 IoT SDK to Mynewt, as discussed earlier.</p>
<h2 id="testing-the-jumper"><a class="doc-anchor" href="#testing-the-jumper">§</a>15.2 Testing the Jumper</h2>
<p>We could test GPIO Input with PineCones onboard jumper.</p>
<p>This should be straightforward, right after we port over the GPIO HAL to Mynewt.</p>
<h2 id="testing-the-uart-port"><a class="doc-anchor" href="#testing-the-uart-port">§</a>15.3 Testing the UART Port</h2>
<p>PineCones UART Port is wired to the USB Connector. We could test PineCones UART Port over USB.</p>
<p>Well need to port the UART HAL from the BL602 IoT SDK to Mynewt.</p>
<p><img src="https://lupyuen.github.io/images/mynewt-furry.jpg" alt="Furry PineCone" /></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>16 Whats Next</h1>
<p>Theres more work to be done porting Mynewt to PineCone…</p>
<ol>
<li>
<p><strong>Port the Hardware Abstraction Layer</strong> from BL602 IoT SDK to Mynewt: GPIO, UART, PWM, I2C, SPI…</p>
</li>
<li>
<p><strong>Bluetooth LE</strong>: We shall reverse engineer the Bluetooth LE Stack on PineCone. Then replace it by the open source <a href="https://github.com/apache/mynewt-nimble"><strong>NimBLE Stack</strong></a>.</p>
</li>
<li>
<p><strong>WiFi</strong>: Also needs to be reverse engineered. We might be able to port this Mynewt WiFi Driver to PineCone…</p>
<ul>
<li>
<p><a href="https://github.com/runtimeco/mynewt_arduino_zero/tree/master/libs/winc1500"><code>mynewt_arduino_zero/ libs/winc1500</code></a></p>
</li>
<li>
<p><a href="https://github.com/runtimeco/mynewt_arduino_zero/tree/master/apps/winc1500_wifi"><code>mynewt_arduino_zero/ apps/winc1500_wifi</code></a></p>
</li>
</ul>
</li>
<li>
<p><strong>Rust</strong> will be supported so that we may build complex firmware without falling into traps with C Pointers.</p>
</li>
</ol>
<p>Then we shall have a fully <strong>Open Source Operating System for PineCone!</strong></p>
<p><em>How confident are we of porting Mynewt to PineCone BL602?</em></p>
<p>One year ago I <a href="https://lupyuen.github.io/articles/hey-gd32-vf103-on-risc-v-i-surrender-for-now">failed to port Mynewt</a> to an earlier RISC-V Microcontroller (GD32 VF103)</p>
<p><em>But Second Times The Charm!</em></p>
<p>PineCones BL602 Microcontroller runs on a RISC-V Core thats similar to SiFive FE310. And porting Mynewt from FE310 to BL602 seems quick and easy. <a href="https://twitter.com/MisterTechBlog/status/1338759961526951937?s=19">(As seen on Twitter)</a></p>
<p>The port of Mynewt to PineCone BL602 continues here…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/book">Read “The RISC-V BL602 Book”</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor">Sponsor me a coffee</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io">Check out my articles</a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>
</li>
</ul>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/mynewt.md"><code>lupyuen.github.io/src/mynewt.md</code></a></p>
<h1 id="appendix-load-firmware-to-cache-memory-not-flash-memory"><a class="doc-anchor" href="#appendix-load-firmware-to-cache-memory-not-flash-memory">§</a>17 Appendix: Load Firmware to Cache Memory, not Flash Memory</h1>
<p><a href="https://lupyuen.github.io/articles/rust"><strong>UPDATE:</strong> We have a new way to create Rust Firmware with BL602 IoT SDK and flash to BL602 Flash Memory over UART, check this out</a></p>
<p><a href="https://github.com/9names/bl602-rust-example"><strong>UPDATE:</strong> Check out this Rust Firmware that runs in Flash Memory instead of Cache Memory</a></p>
<p><em>Why did we load our Mynewt Firmware (and Rust Firmware) to Cache Memory instead of Flash Memory?</em></p>
<p>Because OpenOCD couldnt load our Mynewt Firmware (and Rust Firmware) into Flash Memory.</p>
<p>(Probably because of Flash Protection. Or because writing to BL602 Flash Memory hasnt been implemented in OpenOCD.)</p>
<p><a href="https://github.com/bouffalolab/bl_docs/tree/main/BL602_Openocd&amp;GDB/en">BL602 OpenOCD with JTAG doesnt support loading firmware into Flash Memory. See the BL602 OpenOCD Docs</a></p>
<p><em>What happens when we use <code>blflash</code> to load our firmware to Flash Memory?</em></p>
<p>Were not sure if the Mynewt (or Rust) Firmware will run… But its worth trying!</p>
<p>Make sure that we update the Memory Map to load our code into the XIP Flash Memory at <code>0x2300 0000</code>.</p>
<p>Note that the <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/components/bl602/bl602/evb/src/boot/gcc/start.S">Start Code from BL602 IoT SDK <code>start.S</code></a> references the Boot2 Bootloader.</p>
<p>Were not sure why. Our Start Code from Mynewt (and Rust) doesnt use the Boot2 Bootloader.</p>
<p><img src="https://lupyuen.github.io/images/mynewt-flash.png" alt="Loading Mynewt Firmware to Flash Memory" /></p>
<p><em>What happens when we use OpenOCD + JTAG to load our firmware to Flash Memory?</em></p>
<p>The screen above shows the first version of the Mynewt Firmware, that loads into Flash Memory.</p>
<p>We used this GDB command to dump out the first 10 words of PineCones Flash Memory…</p>
<div class="example-wrap"><pre class="language-text"><code>x/10x _reset_handler</code></pre></div>
<p>(<code>_reset_handler</code> is the function name of Mynewts Start Code, located at the start of our firmware)</p>
<p>When we compare the dumped data with our Firmware Disassembly, we see that the bytes dont match.</p>
<p>Hence we deduce that our Mynewt Firmware wasnt loaded correctly into Flash Memory.</p>
<p><img src="https://lupyuen.github.io/images/mynewt-ram.png" alt="Loading Mynewt Firmware to Cache Memory" /></p>
<p><em>What happens when we use OpenOCD + JTAG to load our firmware to Cache Memory?</em></p>
<p>Heres the second try, loading our Mynewt Firmware to Cache Memory. (The same way that we loaded Rust Firmware in our previous article)</p>
<p>Entering the same GDB Command…</p>
<div class="example-wrap"><pre class="language-text"><code>x/10x _reset_handler</code></pre></div>
<p>We see that the data is identical. Our Mynewt Firmware is loaded correctly to Cache Memory indeed!</p>
<p><em>But we cant run Mynewt Firmware in Cache Memory forever right?</em></p>
<p>The solution is to load our firmware to PineCone over USB (UART). (And flipping the jumper)</p>
<p>We may integrate with VSCode the command-line scripts for loading our firmware to PineCone.</p>
<p>Check out the article…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/flash">“Flashing Firmware to PineCone BL602”</a></li>
</ul>
<h1 id="appendix-install-newt"><a class="doc-anchor" href="#appendix-install-newt">§</a>18 Appendix: Install newt</h1>
<p>We may install Mynewts <code>newt</code> tool according to the instructions here…</p>
<ul>
<li><a href="https://mynewt.apache.org/latest/newt/install/index.html">Installing <code>newt</code></a></li>
</ul>
<p>Or we may build from the source code…</p>
<h2 id="linux-and-macos"><a class="doc-anchor" href="#linux-and-macos">§</a>18.1 Linux and macOS</h2>
<ol>
<li>
<p>Install the <a href="https://golang.org/dl/">latest version of Go</a></p>
</li>
<li>
<p>At a command prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>cd /tmp
export mynewt_version=mynewt_1_8_0_tag
git clone --branch $mynewt_version https://github.com/apache/mynewt-newt/
cd mynewt-newt
./build.sh
sudo mv newt/newt /usr/local/bin
newt version</code></pre></div></li>
<li>
<p>We should see…</p>
<div class="example-wrap"><pre class="language-text"><code>Apache Newt 1.8.0</code></pre></div></li>
</ol>
<h2 id="windows"><a class="doc-anchor" href="#windows">§</a>18.2 Windows</h2>
<p>The Windows version of <code>newt</code> is already bundled at…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt\newt\newt.exe</code></pre></div>
<p>The build script <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/scripts/build-app.cmd"><code>build-app.cmd</code></a> uses the above <code>newt</code> executable.</p>
<p>However, the <code>newt</code> executable triggers a Windows Defender warning (because it wasnt built as a certified executable). We need to <strong>update the Windows Security settings</strong> to allow the <code>newt</code> executable to run.</p>
<p>To build <code>newt</code> from the source code, follow these steps…</p>
<ol>
<li>
<p>Install the <a href="https://golang.org/dl/">latest version of Go</a></p>
</li>
<li>
<p>At a command prompt, enter…</p>
<div class="example-wrap"><pre class="language-cmd"><code>git clone --branch mynewt_1_8_0_tag https://github.com/apache/mynewt-newt/
cd mynewt-newt\newt
go build
newt.exe version</code></pre></div></li>
<li>
<p>We should see…</p>
<div class="example-wrap"><pre class="language-text"><code>Apache Newt 1.8.0</code></pre></div></li>
<li>
<p>Copy the <code>newt</code> executable from…</p>
<div class="example-wrap"><pre class="language-text"><code>mynewt-newt\newt\newt.exe</code></pre></div>
<p>To…</p>
<div class="example-wrap"><pre class="language-text"><code>pinecone-rust-mynewt\newt\newt.exe</code></pre></div></li>
</ol>
<p><img src="https://lupyuen.github.io/images/mynewt-windows.png" alt="Mynewt BL602 built with Windows CMD" /></p>
<p><em>Mynewt BL602 built with Windows CMD</em></p>
<h1 id="appendix-create-the-mynewt-firmware"><a class="doc-anchor" href="#appendix-create-the-mynewt-firmware">§</a>19 Appendix: Create the Mynewt Firmware</h1>
<p>Mynewt Project <code>pinecone-rust-mynewt</code> and Mynewt Firmware <code>pinecone_app</code> were originally created using these steps…</p>
<div class="example-wrap"><pre class="language-bash"><code>newt new pinecone-rust-mynewt
cd pinecone-rust-mynewt
newt upgrade
newt target create pinecone_app
newt target set pinecone_app app=apps/blinky
## This will be changed to pinecone later
newt target set pinecone_app bsp=@apache-mynewt-core/hw/bsp/hifive1
newt target set pinecone_app build_profile=debug</code></pre></div>
<p>We dont need to create them again, just download from…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt"><code>github.com/lupyuen/pinecone-rust-mynewt</code></a></li>
</ul>
<p>The steps above were based on the <a href="https://mynewt.apache.org/latest/tutorials/blinky/blinky_stm32f4disc.html">Blinky Tutorial for STM32F4-Discovery</a>.</p>
<p>I added this Git Modules file so that the Mynewt source files will be downloaded together with the repo…</p>
<ul>
<li><a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/.gitmodules"><code>.gitmodules</code></a></li>
</ul>
<h1 id="appendix-vscode-settings"><a class="doc-anchor" href="#appendix-vscode-settings">§</a>20 Appendix: VSCode Settings</h1><h2 id="debugger-settings"><a class="doc-anchor" href="#debugger-settings">§</a>20.1 Debugger Settings</h2>
<p>The VSCode Debugger Settings may be found in <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/.vscode/launch.json"><code>.vscode/launch.json</code></a></p>
<p>This file defines…</p>
<ul>
<li>
<p>Firmware Path (<code>target</code>)</p>
</li>
<li>
<p>GDB Path (<code>gdbpath</code>)</p>
</li>
<li>
<p>OpenOCD Path (in <code>autorun</code>, after <code>target remote</code>)</p>
</li>
<li>
<p>GDB Commands to be executed upon starting the debugger (<code>autorun</code>)</p>
</li>
</ul>
<div class="example-wrap"><pre class="language-json"><code>{
// VSCode Debugger Config for PineCone BL602
&quot;version&quot;: &quot;0.2.0&quot;,
&quot;configurations&quot;: [
{
&quot;name&quot;: &quot;BL602&quot;,
&quot;type&quot;: &quot;gdb&quot;,
&quot;request&quot;: &quot;launch&quot;,
// Application Executable to be flashed before debugging
&quot;target&quot;: &quot;${workspaceRoot}/bin/targets/pinecone_app/app/apps/blinky/blinky.elf&quot;,
&quot;cwd&quot;: &quot;${workspaceRoot}&quot;,
&quot;gdbpath&quot;: &quot;${workspaceRoot}/xpack-riscv-none-embed-gcc/bin/riscv-none-embed-gdb&quot;,
&quot;valuesFormatting&quot;: &quot;parseText&quot;,
&quot;autorun&quot;: [
// Before loading the Application, run these gdb commands.
// Set timeout for executing openocd commands.
&quot;set remotetimeout 600&quot;,
// This indicates that an unrecognized breakpoint location should automatically result in a pending breakpoint being created.
&quot;set breakpoint pending on&quot;,
// Set breakpoints
&quot;break main&quot;, // Break at main()
&quot;break __assert_func&quot;, // Break for any C assert failures
// &quot;break os_default_irq&quot;, // Break for any Mynewt unhandled interrupts
// &quot;break core::panicking::panic&quot;, // Break for any Rust assert failures and panics
// &quot;break core::result::unwrap_failed&quot;, // Break for any Rust unwrap and expect failures
// Launch OpenOCD. Based on https://www.justinmklam.com/posts/2017/10/vscode-debugger-setup/
&quot;target remote | xpack-openocd/bin/openocd -c \&quot;gdb_port pipe; log_output openocd.log\&quot; -f openocd.cfg &quot;,
// Load the program into board memory
&quot;load&quot;,
// Execute one RISC-V instruction and stop
// &quot;stepi&quot;,
// Run the program until we hit the main() breakpoint
// &quot;continue&quot;,
]
}
]
}</code></pre></div><h2 id="task-settings"><a class="doc-anchor" href="#task-settings">§</a>20.2 Task Settings</h2>
<p>The VSCode Task Settings may be found in <a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/.vscode/tasks.json"><code>.vscode/tasks.json</code></a></p>
<p>This file defines the VSCode Task for building the Mynewt Firmware…</p>
<div class="example-wrap"><pre class="language-json"><code>{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
&quot;version&quot;: &quot;2.0.0&quot;,
&quot;tasks&quot;: [
{
// Build firmware
&quot;label&quot;: &quot;Build Firmware&quot;,
&quot;type&quot;: &quot;shell&quot;,
&quot;windows&quot;: {
&quot;command&quot;: &quot;cmd&quot;,
&quot;args&quot;: [
&quot;/c&quot;,
&quot; newt build pinecone_app &amp;&amp; newt size -v pinecone_app &amp;&amp; echo ✅ ◾ Done! &quot;
]
},
&quot;osx&quot;: {
&quot;command&quot;: &quot;bash&quot;,
&quot;args&quot;: [
&quot;-c&quot;, &quot;-l&quot;,
&quot; scripts/build-app.sh &amp;&amp; echo ✅ ◾ Done! &quot;
]
},
&quot;linux&quot;: {
&quot;command&quot;: &quot;bash&quot;,
&quot;args&quot;: [
&quot;-c&quot;, &quot;-l&quot;,
&quot; scripts/build-app.sh &amp;&amp; echo ✅ ◾ Done! &quot;
]
},
&quot;group&quot;: {
&quot;kind&quot;: &quot;build&quot;,
&quot;isDefault&quot;: true
},
&quot;problemMatcher&quot;: [
{
// Problem matcher for GNU Linker, e.g. /Users/Luppy/mynewt/stm32bluepill-mynewt-sensor/apps/my_sensor_app/src/ATParser.h:82: undefined reference to `operator delete[](void*)&#39;
&quot;fileLocation&quot;: [ &quot;absolute&quot; ],
&quot;pattern&quot;: {
&quot;regexp&quot;: &quot;^(/.*):(\\d+):\\s+(.*)$&quot;,
&quot;file&quot;: 1,
&quot;line&quot;: 2,
&quot;message&quot;: 3,
// &quot;code&quot;: 3,
// &quot;severity&quot;: 4,
}
}
],
&quot;presentation&quot;: {
&quot;clear&quot;: true
}
},
...</code></pre></div>
<p><a href="https://github.com/lupyuen/pinecone-rust-mynewt/blob/main/scripts/build-app.sh"><code>scripts/build-app.sh</code></a> does the following…</p>
<ol>
<li>
<p>Terminate the OpenOCD process</p>
</li>
<li>
<p>Build the Mynewt Firmware</p>
</li>
<li>
<p>Display the firmware size</p>
</li>
</ol>
<div class="example-wrap"><pre class="language-bash"><code>#!/usr/bin/env bash
## macOS and Linux Bash script to build Mynewt Firmware
set -e # Exit when any command fails
set -x # Echo commands
## Terminate any OpenOCD processes from the debug session
set +e # Ignore errors
pkill openocd
set -e # Stop on errors
## Add GCC to the PATH
set +x # Stop echo
export PATH=&quot;$PWD/xpack-riscv-none-embed-gcc/bin:$PATH&quot;
set -x # Echo commands
## Build the Mynewt Firmware
newt build pinecone_app
## Display the firmware size
newt size -v pinecone_app</code></pre></div>
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker and Prism Theme -->
<script src="../theme.js"></script>
<script src="../prism.js"></script>
<!-- Theme Picker and Prism Theme -->
<!-- End scripts/rustdoc-after.html -->
</body>
</html>