In the most recent post in the RISC-V Bytes series, we had our first experience with real hardware, exploring the bootloader on an ESP32-C3-DevKitC-02 module. We were using esp-idf, which, behind the scenes uses an implementation of FreeRTOS. In this post, we’ll swap out the Espressif FreeRTOS implementation for Zephyr, exploring some similarities and differences between the build process and produced artifacts. We’ll also see how we can slim down a Zephyr installation, only fetching the code and tools that we need to target the ESP32-C3 chipset.
Please excuse my attempt at an illustration of
esptool.py
inside of a Trojan Horse.
Sections Link to heading
Installing Tools Link to heading
Zephyr encourages the use of
west
, which
essentially is an extensible wrapper around a variety of other tools. If you are
familiar with the underlying tools, using west
can feel a bit cumbersome, but
opting out of doing so means that much of the guides and documentation will not
be applicable to your workflow. I found myself using west
in somewhat of a
minimal fashion, but not going fully into using Zephyr without
west
.
west
-based workflows allow developing Zephyr applications via three different
models:
repository
: building an application within the Zephyr repositoryworkspace
: building an application in the same workspace as the Zephyr repository (i.e. sharing a parent directory)freestanding
: building an application that is configured to point to some installation of Zephyr
While repository
seems to be the most straightforward for building the in-tree
sample applications, and workspace
appears to be the recommended route for
most projects, I found freestanding
to be more familiar to my typical
development workflow. That being said, there are pros and cons of all three
options and you should select the one that is most appropriate for your project.
We’ll explore the resulting directory structure when using a freestanding
model in the next section.
Initial tooling setup mostly looks the same regardless of what model you are
going to use. The full steps for installing cmake
, python
, and dtc
are
outlined in the Getting Started
Guide.
I opted, partially due to my desire to use the freestanding
model, to do a
global installation of west
.
$ pip3 install --user -U west
Setting up a Workspace Link to heading
With tools in place, it is now time to download Zephyr itself. Rather than using
west init ~/zephyrproject
, which will clone the latest Zephyr version into
~/zephyrproject/zephyr
, I created a new subdirectory in my existing
~/code/github.com/zephyrproject
directory for workspaces, and a subdirectory
within it for the workspace we will use for the remainder of this blog post.
$ mkdir -p workspaces/v3.3.0
As the name suggests, this workspace will be set up to with Zephyr v3.3.0
,
which is the latest release. Within the v3.3.0
directory, rather than
running a bare west init
as the guide suggests, we’ll create a manifest
repository, which will inform the version of Zephyr we want and any additional
dependencies.
Note: if we were using the
workspace
application model, the manifest repository could be our application repository itself. If we were using therepository
application model, the manifest repository would be Zephyr.
v3.3.0/manifest/west.yml
manifest:
projects:
- name: zephyr
revision: v3.3.0
url: https://github.com/zephyrproject-rtos/zephyr
west-commands: scripts/west-commands.yml
import:
name-allowlist:
- hal_espressif
Here we are specifying that we want the v3.3.0
release of Zephyr, and we only
want the hal_espressif
project imported from the list of projects in its
west.yml
. Now we can run west init
in the v3.3.0
directory and point
it to our local manifest repository.
$ west init -l manifest/
=== Initializing from existing manifest repository manifest
--- Creating /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/.west and local configuration file
=== Initialized. Now run "west update" inside /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0.
All this will do is setup west
to use the local manifest for future
operations. Following the instructions in the output, along with some extra
flags, will download Zephyr and the hal_espressif
.
$ west update --narrow -o=--depth=1
=== updating zephyr (zephyr):
--- zephyr: initializing
Initialized empty Git repository in /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/.git/
--- zephyr: fetching, need revision v3.3.0
remote: Enumerating objects: 28549, done.
remote: Counting objects: 100% (28549/28549), done.
remote: Compressing objects: 100% (22159/22159), done.
remote: Total 28549 (delta 6571), reused 14659 (delta 4784), pack-reused 0
Receiving objects: 100% (28549/28549), 57.04 MiB | 23.41 MiB/s, done.
Resolving deltas: 100% (6571/6571), done.
From https://github.com/zephyrproject-rtos/zephyr
* tag v3.3.0 -> FETCH_HEAD
Updating files: 100% (24352/24352), done.
HEAD is now at 07c6af3b release: Zephyr 3.3.0
HEAD is now at 07c6af3b release: Zephyr 3.3.0
=== updating hal_espressif (modules/hal/espressif):
--- hal_espressif: initializing
Initialized empty Git repository in /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/.git/
--- hal_espressif: fetching, need revision 73e7af1e2ed64571ce49ff9f07dc02690b9f2df5
remote: Enumerating objects: 11222, done.
remote: Counting objects: 100% (11222/11222), done.
remote: Compressing objects: 100% (9234/9234), done.
remote: Total 11222 (delta 2547), reused 6008 (delta 1450), pack-reused 0
Receiving objects: 100% (11222/11222), 64.90 MiB | 22.79 MiB/s, done.
Resolving deltas: 100% (2547/2547), done.
From https://github.com/zephyrproject-rtos/hal_espressif
* branch 73e7af1e2ed64571ce49ff9f07dc02690b9f2df5 -> FETCH_HEAD
Updating files: 100% (9878/9878), done.
HEAD is now at 73e7af1 hal: clock: esp32c3: Fix default cpu frequency magnitude
HEAD is now at 73e7af1 hal: clock: esp32c3: Fix default cpu frequency magnitude
Without the name-allowlist
restriction, we would instead download all
projects in the Zephyr west.yml
, which is much more than we need. There is a
tradeoff in this slimmer approach in that if we wanted to target multiple
chipsets, or just expand functionality, with the same project, we would need to
either add to the allowlist, or setup a new workspace. The philosophy of
Zephyr’s getting started guide ensures that users will have everything they need
when getting started, but it also means that they will likely download a number
of projects they will never use.
The --narrow
and -o=--depth=1
flags are also useful in reducing the amount
of content we download and store. Without the former,
--tags
is passed to git fetch
,
causing all tags to be fetched from the remote. The latter passes the
--depth
flag to git fetch
.
The result of these operations can be observed by viewing the git
history of
Zephyr and hal_espressif
.
$ git -C zephyr/ log
commit 07c6af3b8c35c1e49186578ca61a25c76e2fb308 (grafted, HEAD, manifest-rev)
Author: Stephanos Ioannidis <root@stephanos.io>
Date: Sat Feb 18 07:13:22 2023 +0900
release: Zephyr 3.3.0
This commit sets the Zephyr version to v3.3.0.
Signed-off-by: Stephanos Ioannidis <root@stephanos.io>
Signed-off-by: Lauren Murphy <lauren.murphy@intel.com>
$ git -C modules/hal/espressif/ log
commit 73e7af1e2ed64571ce49ff9f07dc02690b9f2df5 (grafted, HEAD, manifest-rev)
Author: Lucas Tamborrino <lucas.tamborrino@espressif.com>
Date: Mon Jan 9 10:22:24 2023 -0300
hal: clock: esp32c3: Fix default cpu frequency magnitude
The value must be corrected in hal after fetching it from dts
Signed-off-by: Lucas Tamborrino <lucas.tamborrino@espressif.com>
Note: If your application depends on functionality that is only available in binary form, it may be necessary to fetch blobs. Examples of blobs that may be required when targeting ESP32 systems can be found in the
hal_espressif
module.yml
file, and instructions for how to fetch are present in the ESP32-C3 guide.
This may not be a great strategy for other project layouts where it is expected that versions may be updated. However, if you plan to use a single version per workspace strategy, as we have chosen here, we are not concerned about having a partial history. The only subsequent update operations we should be performing in this workspace would be to add a new project, which would not require any changes to the existing Zephyr installation or any modules.
The last steps are to export this version of Zephyr as a CMake package and
install all Python dependencies. The latter can be done with the following
command, which will tell CMake where to look for configuration for this version
of Zephyr. This will allow us to add the Zephyr package to our freestanding
project a bit later.
$ west zephyr-export
Zephyr (/home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/share/zephyr-package/cmake)
has been added to the user package registry in:
~/.cmake/packages/Zephyr
ZephyrUnittest (/home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/share/zephyrunittest-package/cmake)
has been added to the user package registry in:
~/.cmake/packages/ZephyrUnittest
Installing Python dependencies is a simple as doing a pip install
on the
Zephyr repository’s requirements.txt
.
pip3 install --user -r zephyr/scripts/requirements.txt
Getting a Toolchain Link to heading
We have all of the code we will need, but we need a toolchain in order to build our image. Zephyr has made this quite straightforward via the Zephyr SDK. It provides toolchains for every Zephyr supported target. However, the getting started guide will once again have you install all of them by default. This is RISC-V Bytes and we only need our RISC-V toolchain to target the ESP32-C3, so we’ll instead use the minimal bundle and only specify the toolchain we want when performing setup.
$ cd ~
$ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.0/zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz
--2023-04-16 20:46:36-- https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.0/zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz
Resolving github.com (github.com)... 140.82.113.3
Connecting to github.com (github.com)|140.82.113.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/92793986/dfb8508d-575a-412c-ad6a-b758f91650fa?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230417%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230417T004638Z&X-Amz-Expires=300&X-Amz-Signature=8392a1e3072e6ebc2c9c4e1ce83b91e971fc3de99f3733fa62b87e4b8bcf0885&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=92793986&response-content-disposition=attachment%3B%20filename%3Dzephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz&response-content-type=application%2Foctet-stream [following]
--2023-04-16 20:46:36-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/92793986/dfb8508d-575a-412c-ad6a-b758f91650fa?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230417%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230417T004638Z&X-Amz-Expires=300&X-Amz-Signature=8392a1e3072e6ebc2c9c4e1ce83b91e971fc3de99f3733fa62b87e4b8bcf0885&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=92793986&response-content-disposition=attachment%3B%20filename%3Dzephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz&response-content-type=application%2Foctet-stream
Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 41512804 (40M) [application/octet-stream]
Saving to: ‘zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz’
zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz 100%[===============================================================================================================>] 39.59M 27.6MB/s in 1.4s
2023-04-16 20:46:38 (27.6 MB/s) - ‘zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz’ saved [41512804/41512804]
$ wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.0/sha256.sum | shasum --check --ignore-missing
--2023-04-16 20:46:38-- https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.0/sha256.sum
Resolving github.com (github.com)... 140.82.113.3
Connecting to github.com (github.com)|140.82.113.3|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/92793986/e2809f41-5011-41dc-a209-4fe00924abb5?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230417%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230417T004645Z&X-Amz-Expires=300&X-Amz-Signature=91a3b3d1be93ebd02b64d993b32ed9a2db4b48d333b8e3ebf36af319d1b2f132&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=92793986&response-content-disposition=attachment%3B%20filename%3Dsha256.sum&response-content-type=application%2Foctet-stream [following]
--2023-04-16 20:46:43-- https://objects.githubusercontent.com/github-production-release-asset-2e65be/92793986/e2809f41-5011-41dc-a209-4fe00924abb5?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230417%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230417T004645Z&X-Amz-Expires=300&X-Amz-Signature=91a3b3d1be93ebd02b64d993b32ed9a2db4b48d333b8e3ebf36af319d1b2f132&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=92793986&response-content-disposition=attachment%3B%20filename%3Dsha256.sum&response-content-type=application%2Foctet-stream
Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.109.133, 185.199.111.133, 185.199.110.133, ...
Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12148 (12K) [application/octet-stream]
Saving to: ‘STDOUT’
- 100%[===============================================================================================================>] 11.86K --.-KB/s in 0.001s
2023-04-16 20:46:48 (15.7 MB/s) - written to stdout [12148/12148]
zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz: OK
The minimal bundle includes a setup.sh
script that will prompt the user for
toolchains that should be installed. We can extract the bundle, then run
setup.sh
.
$ tar xvf zephyr-sdk-0.16.0_linux-x86_64_minimal.tar.xz -C ~/.local/
zephyr-sdk-0.16.0/
zephyr-sdk-0.16.0/sdk_version
zephyr-sdk-0.16.0/sdk_toolchains
zephyr-sdk-0.16.0/cmake/
zephyr-sdk-0.16.0/cmake/Zephyr-sdkConfig.cmake
zephyr-sdk-0.16.0/cmake/Zephyr-sdkConfigVersion.cmake
zephyr-sdk-0.16.0/cmake/zephyr/
zephyr-sdk-0.16.0/cmake/zephyr/Kconfig
zephyr-sdk-0.16.0/cmake/zephyr/generic.cmake
zephyr-sdk-0.16.0/cmake/zephyr/host-tools.cmake
zephyr-sdk-0.16.0/cmake/zephyr/target.cmake
zephyr-sdk-0.16.0/cmake/zephyr_sdk_export.cmake
zephyr-sdk-0.16.0/zephyr-sdk-x86_64-hosttools-standalone-0.9.sh
zephyr-sdk-0.16.0/setup.sh
$ ~/.local/zephyr-sdk-0.16.0/setup.sh
Zephyr SDK 0.16.0 Setup
** NOTE **
You only need to run this script once after extracting the Zephyr SDK
distribution bundle archive.
Install toolchains for all targets [y/n]? n
Install 'aarch64-zephyr-elf' toolchain [y/n]? n
Install 'arc64-zephyr-elf' toolchain [y/n]? n
Install 'arc-zephyr-elf' toolchain [y/n]? n
Install 'arm-zephyr-eabi' toolchain [y/n]? n
Install 'microblazeel-zephyr-elf' toolchain [y/n]? n
Install 'mips-zephyr-elf' toolchain [y/n]? n
Install 'nios2-zephyr-elf' toolchain [y/n]? n
Install 'riscv64-zephyr-elf' toolchain [y/n]? y
Install 'sparc-zephyr-elf' toolchain [y/n]? n
Install 'x86_64-zephyr-elf' toolchain [y/n]? n
Install 'xtensa-espressif_esp32_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-espressif_esp32s2_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-espressif_esp32s3_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-intel_apl_adsp_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-intel_s1000_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-nxp_imx_adsp_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-nxp_imx8m_adsp_zephyr-elf' toolchain [y/n]? n
Install 'xtensa-sample_controller_zephyr-elf' toolchain [y/n]? n
Install host tools [y/n]? y
Register Zephyr SDK CMake package [y/n]? y
Installing 'riscv64-zephyr-elf' toolchain ...
toolchain_linux-x86_64_riscv64-zephyr-elf.tar.xz 100%[===============================================================================================================>] 105.29M 35.9MB/s in 2.9s
Installing host tools ...
Registering Zephyr SDK CMake package ...
Zephyr-sdk (/home/hasheddan/.local/zephyr-sdk-0.16.0/cmake)
has been added to the user package registry in:
~/.cmake/packages/Zephyr-sdk
All done.
Press any key to exit ...
Note: You may have noticed that the installed toolchain is for 64-bit RISC-V (riscv64), but we are targeting a 32-bit chipset. The 64-bit toolchain can target both 32-bit and 64-bit architectures (as specified by flags).
You should see all RISC-V tools now present.
$ ls ~/.local/zephyr-sdk-0.16.0/riscv64-zephyr-elf/bin/
riscv64-zephyr-elf-addr2line riscv64-zephyr-elf-cpp riscv64-zephyr-elf-gcc-ar riscv64-zephyr-elf-gdb riscv64-zephyr-elf-ld riscv64-zephyr-elf-ranlib
riscv64-zephyr-elf-ar riscv64-zephyr-elf-ct-ng.config riscv64-zephyr-elf-gcc-nm riscv64-zephyr-elf-gdb-add-index riscv64-zephyr-elf-ld.bfd riscv64-zephyr-elf-readelf
riscv64-zephyr-elf-as riscv64-zephyr-elf-elfedit riscv64-zephyr-elf-gcc-ranlib riscv64-zephyr-elf-gdb-add-index-py riscv64-zephyr-elf-lto-dump riscv64-zephyr-elf-size
riscv64-zephyr-elf-c++ riscv64-zephyr-elf-g++ riscv64-zephyr-elf-gcov riscv64-zephyr-elf-gdb-py riscv64-zephyr-elf-nm riscv64-zephyr-elf-strings
riscv64-zephyr-elf-cc riscv64-zephyr-elf-gcc riscv64-zephyr-elf-gcov-dump riscv64-zephyr-elf-gprof riscv64-zephyr-elf-objcopy riscv64-zephyr-elf-strip
riscv64-zephyr-elf-c++filt riscv64-zephyr-elf-gcc-12.2.0 riscv64-zephyr-elf-gcov-tool riscv64-zephyr-elf-gprof-py riscv64-zephyr-elf-objdump
Building the Image Link to heading
After quite a bit of setup, we are now ready to build! We can setup a simple
freestanding
repository that builds a similar image to the sample
hello_world
application.
Create a new directory outside of any Zephyr workspace (I chose
~/code/github.com/hasheddan/zephyr-freestanding
), and create a
CMakeLists.txt
file, as well as src/main.c
.
$ mkdir -p zephyr-freestanding/src
$ cd zephyr-freestanding && touch CMakeLists.txt src/main.c
$ tree
.
├── CMakeLists.txt
└── src
└── main.c
1 directory, 2 files
All that our application will do is print a message and exit. Including
zepher/kernel.h
gives us access to printk
, which allows for printing kernel debugging messages.
src/main.c
#include <zephyr/kernel.h>
int main(void)
{
printk("Hello, RISC-V Bytes!\n");
return 0;
}
In CMakeLists.txt
, we’ll need to inform the build system where to find our
Zephyr installation, as well as where the source(s) for our application live.
CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr 3.3.0 REQUIRED)
project(zephyr-freestanding)
target_sources(app PRIVATE src/main.c)
CMake will be able to identify the Zephyr
package due to the west zephyr-export
command we ran earlier. We add the version restriction (3.3.0
)
and the REQUIRED
directive such that we will fail early if CMake is unable to
identify a suitable package. However, in order to use CMake via the recommended
west build
command, we’ll still need to set ZEPHYR_BASE
. This is due to the
fact that the build
command is implemented as a plugin that lives in the
Zephyr
repository,
rather than a built-in command. The only argument that needs to be supplied is
--board
, which informs our target, and correspondingly, toolchain and required
modules. Let’s run the build and analyze the output.
Note: We are specifying
esp32c3_devkitm
despite using the ESP32-C3-DevKitC-02. Because these two boards both use the ESP32-C3 chip, the configuration is mostly the same.
$ export ZEPHYR_BASE=~/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr
$ west build -b esp32c3_devkitm
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding
-- CMake version: 3.22.1
-- Found Python3: /usr/bin/python3.10 (found suitable exact version "3.10.6") found components: Interpreter
-- Cache files will be written to: /home/hasheddan/.cache/zephyr
-- Zephyr version: 3.3.0 (/home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr)
-- Found west (found suitable version "1.0.0", minimum required is "0.7.1")
-- Board: esp32c3_devkitm
-- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK
-- Found host-tools: zephyr 0.16.0 (/home/hasheddan/.local/zephyr-sdk-0.16.0)
-- Found toolchain: zephyr 0.16.0 (/home/hasheddan/.local/zephyr-sdk-0.16.0)
-- Found Dtc: /home/hasheddan/.local/zephyr-sdk-0.16.0/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6")
-- Found BOARD.dts: /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/boards/riscv/esp32c3_devkitm/esp32c3_devkitm.dts
-- Generated zephyr.dts: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/zephyr.dts
-- Generated devicetree_generated.h: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/include/generated/devicetree_generated.h
-- Including generated dts.cmake file: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/dts.cmake
Parsing /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/Kconfig
Loaded configuration '/home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr/boards/riscv/esp32c3_devkitm/esp32c3_devkitm_defconfig'
Configuration saved to '/home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/.config'
Kconfig header saved to '/home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 12.2.0
-- The CXX compiler identification is GNU 12.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/hasheddan/.local/zephyr-sdk-0.16.0/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Configuring done
-- Generating done
-- Build files have been written to: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build
-- west build: building application
[1/179] Preparing syscall dependency handling
[2/179] Generating include/generated/version.h
-- Zephyr version: 3.3.0 (/home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/zephyr), build: 07c6af3b8c35
[162/179] Performing configure step for 'EspIdfBootloader'
-- Found Git: /usr/bin/git (found version "2.34.1")
...
-- Building ESP-IDF components for target esp32c3
-- Project sdkconfig file /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/sdkconfig
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/soc/esp32c3/ld/esp32c3.peripherals.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_rom/esp32c3/ld/esp32c3.rom.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_rom/esp32c3/ld/esp32c3.rom.api.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_rom/esp32c3/ld/esp32c3.rom.libgcc.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_rom/esp32c3/ld/esp32c3.rom.newlib.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader/subproject/main/ld/esp32c3/bootloader.ld
-- Adding linker script /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader/subproject/main/ld/esp32c3/bootloader.rom.ld
-- Components: bootloader bootloader_support efuse esp32c3 esp_common esp_hw_support esp_rom esp_system esptool_py freertos hal log main micro-ecc newlib partition_table riscv soc spi_flash
-- Component paths: /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader_support /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/efuse /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp32c3 /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_common /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_hw_support /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_rom /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esp_system /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esptool_py /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/freertos /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/hal /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/log /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader/subproject/main /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/bootloader/subproject/components/micro-ecc /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/newlib /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/partition_table /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/riscv /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/soc /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/spi_flash
-- Configuring done
-- Generating done
-- Build files have been written to: /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/bootloader
[163/179] Performing build step for 'EspIdfBootloader'
[1/88] Generating project_elf_src_esp32c3.c
[2/88] Building C object esp-idf/soc/CMakeFiles/__idf_soc.dir/soc_include_legacy_warn.c.obj
...
[86/88] Linking C executable bootloader.elf
[87/88] Generating binary image from built executable
esptool.py v3.3
Creating esp32c3 image...
Merged 1 ELF section
Successfully created esp32c3 image.
Generated /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/bootloader/bootloader.bin
[88/88] cd /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/bootloader/esp-idf/esptool_py && /usr/bin/python3.10 /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/partition_table/check_sizes.py --offset 0x8000 bootloader 0x0 /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/bootloader/bootloader.bin
Bootloader binary size 0x4ba0 bytes. 0x3460 bytes (41%) free.
[169/179] Linking C executable zephyr/zephyr_pre0.elf
[173/179] Linking C executable zephyr/zephyr_pre1.elf
[179/179] Linking C executable zephyr/zephyr.elf
Memory region Used Size Region Size %age Used
mcuboot_hdr: 32 B 32 B 100.00%
metadata: 28 B 32 B 87.50%
ROM: 32164 B 4194240 B 0.77%
iram0_0_seg: 11376 B 320 KB 3.47%
irom0_0_seg: 83568 B 4194272 B 1.99%
drom0_0_seg: 2008 B 4194240 B 0.05%
dram0_0_seg: 21268 B 320 KB 6.49%
rtc_iram_seg: 0 GB 8 KB 0.00%
IDT_LIST: 0 GB 8 KB 0.00%
esptool.py v3.3
Creating esp32c3 image...
Merged 6 ELF sections
Successfully created esp32c3 image.
If some of this output looks familiar, it is likely because you have seen many
of the same steps when using
esptool.py
/
idf.py
. The
Zephyr build system wraps these tools from
hal_espressif
, which is
essentially a fork of esp-idf
. In
fact, until we get to building the actual Zephyr application, we are building
the same second stage bootloader from esp-idf
that we saw in the last post.
$ ls build/esp-idf/build/bootloader
bootloader.bin bootloader.map CMakeCache.txt cmake_install.cmake config esp-idf kconfigs_projbuild.in project_elf_src_esp32c3.c
bootloader.elf build.ninja CMakeFiles compile_commands.json config.env kconfigs.in project_description.json
If we examine the bootloader.elf
file, we’ll see the same steps to load and
jump to the application image.
$ ~/.local/zephyr-sdk-0.16.0/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-objdump -D -j .iram.text build/esp-idf/build/bootloader/bootloader.elf | head -48
build/esp-idf/build/bootloader/bootloader.elf: file format elf32-littleriscv
Disassembly of section .iram.text:
403ce000 <call_start_cpu0>:
403ce000: 7131 addi sp,sp,-192
403ce002: df06 sw ra,188(sp)
403ce004: 00000793 li a5,0
403ce008: c789 beqz a5,403ce012 <call_start_cpu0+0x12>
403ce00a: 00000097 auipc ra,0x0
403ce00e: 000000e7 jalr zero # 0 <mapped-0x3fcd6000>
403ce012: 288d jal 403ce084 <bootloader_init>
403ce014: c119 beqz a0,403ce01a <call_start_cpu0+0x1a>
403ce016: 444030ef jal ra,403d145a <bootloader_reset>
403ce01a: 00000793 li a5,0
403ce01e: c789 beqz a5,403ce028 <call_start_cpu0+0x28>
403ce020: 00000097 auipc ra,0x0
403ce024: 000000e7 jalr zero # 0 <mapped-0x3fcd6000>
403ce028: 0a000613 li a2,160
403ce02c: 4581 li a1,0
403ce02e: 0808 addi a0,sp,16
403ce030: ffc32097 auipc ra,0xffc32
403ce034: 324080e7 jalr 804(ra) # 40000354 <memset>
403ce038: 0808 addi a0,sp,16
403ce03a: 076030ef jal ra,403d10b0 <bootloader_utility_load_partition_table>
403ce03e: e10d bnez a0,403ce060 <call_start_cpu0+0x60>
403ce040: 5be020ef jal ra,403d05fe <esp_log_early_timestamp>
403ce044: 85aa mv a1,a0
403ce046: 3fcd6637 lui a2,0x3fcd6
403ce04a: 3fcd6537 lui a0,0x3fcd6
403ce04e: 14c60613 addi a2,a2,332 # 3fcd614c <_data_end+0x48>
403ce052: 15450513 addi a0,a0,340 # 3fcd6154 <_data_end+0x50>
403ce056: ffc32097 auipc ra,0xffc32
403ce05a: fea080e7 jalr -22(ra) # 40000040 <esp_rom_printf>
403ce05e: bf65 j 403ce016 <call_start_cpu0+0x16>
403ce060: 0808 addi a0,sp,16
403ce062: 27c030ef jal ra,403d12de <bootloader_utility_get_selected_boot_partition>
403ce066: f9d00793 li a5,-99
403ce06a: c62a sw a0,12(sp)
403ce06c: faf505e3 beq a0,a5,403ce016 <call_start_cpu0+0x16>
403ce070: 4501 li a0,0
403ce072: 2029 jal 403ce07c <bootloader_common_get_reset_reason>
403ce074: 45b2 lw a1,12(sp)
403ce076: 0808 addi a0,sp,16
403ce078: 400030ef jal ra,403d1478 <bootloader_utility_load_boot_image>
The application it loads lives at build/zephyr/zephyr.elf
. We can see the
assembly dump of our main()
function in the .flash.text
section.
$ ~/.local/zephyr-sdk-0.16.0/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-objdump -D -j .flash.text build/zephyr/zephyr.elf | head -20
build/zephyr/zephyr.elf: file format elf32-littleriscv
Disassembly of section .flash.text:
42010020 <_OffsetAbsSyms>:
42010020: 00008067 ret
42010024 <main>:
42010024: 3c000537 lui a0,0x3c000
42010028: ff010113 addi sp,sp,-16
4201002c: 04050513 addi a0,a0,64 # 3c000040 <__rodata_region_start>
42010030: 00112623 sw ra,12(sp)
42010034: fe373097 auipc ra,0xfe373
42010038: ad4080e7 jalr -1324(ra) # 40382b08 <printk>
4201003c: 00c12083 lw ra,12(sp)
42010040: 00000513 li a0,0
42010044: 01010113 addi sp,sp,16
42010048: 00008067 ret
Lastly, we can find our partition table at
build/esp-idf/build/partitions_singleapp.bin
, which looks very similar to the
one we saw when using esp-idf
directly.
$ hexdump build/esp-idf/build/partitions_singleapp.bin
0000000 50aa 0201 9000 0000 6000 0000 766e 0073
0000010 0000 0000 0000 0000 0000 0000 0000 0000
0000020 50aa 0101 f000 0000 1000 0000 6870 5f79
0000030 6e69 7469 0000 0000 0000 0000 0000 0000
0000040 50aa 0000 0000 0001 0000 0010 6166 7463
0000050 726f 0079 0000 0000 0000 0000 0000 0000
0000060 ebeb ffff ffff ffff ffff ffff ffff ffff
0000070 adf4 454f 5638 5d4b 3574 2cb6 b675 2495
0000080 ffff ffff ffff ffff ffff ffff ffff ffff
*
0000c00
Flashing and Running Link to heading
With our three binaries constructed, all that is left to do is flashing and
running. One thing we skipped in the getting started guide was setting up udev
rules that allow us to access the board. The
Zephyr SDK bundles rules from OpenOCD, but we need to
ensure that it includes a rule that gives us access to the USB-to-UART bridge
that lives on the ESP32-C3-DevKitC-02 board. We can use lsusb
to determine the
vendor and product ID for the bridge.
$ lsusb | grep UART
Bus 002 Device 009: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
The bundled rules can be found in the
sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules
file.
There is not a matching entry for the 10c4
vendor ID and ea60
product ID,
but we can add one in the following format.
# Silicon Labs USB-UART bridge
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="660", GROUP="plugdev", TAG+="uaccess"
After doing so, we can copy these rules into the /etc/udev/rules.d/
directory,
then reload the rules.
$ sudo cp ~/.local/zephyr-sdk-0.16.0/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d/
$ sudo udevadm control --reload
Now we are ready to run west flash
. This time we’ll supply the -v
flag to
instruct west
to emit verbose output, which will allow us to see exactly how
it is wrapping our Espressif tools.
Note:
west flash
is also a subcommand provided by Zephyr, not built intowest
. Ensure that yourZEPHYR_BASE
is still set to the proper path to use it.
$ west -v flash
-- west flash: rebuilding
cmake version 3.22.1 is OK; minimum version is 3.13.1
Running CMake: /usr/bin/cmake --build /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build
ninja: no work to do.
-- west flash: using runner esp32
-- runners.esp32: Flashing esp32 chip on None (921600bps)
runners.esp32: /usr/bin/python3 /home/hasheddan/code/github.com/zephyrproject/workspaces/v3.3.0/modules/hal/espressif/components/esptool_py/esptool/esptool.py --chip auto --baud 921600 --before default_reset --after hard_reset write_flash -u --flash_mode dio --flash_freq 40m --flash_size detect 0x0 /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/bootloader/bootloader.bin 0x8000 /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/esp-idf/build/partitions_singleapp.bin 0x10000 /home/hasheddan/code/github.com/hasheddan/zephyr-freestanding/build/zephyr/zephyr.bin
esptool.py v3.3
Found 1 serial ports
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... ESP32-C3
Chip is ESP32-C3 (revision 3)
Features: Wi-Fi
Crystal is 40MHz
MAC: 58:cf:79:16:7d:a0
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00000000 to 0x00004fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x00010000 to 0x00034fff...
Flash params set to 0x0220
Wrote 32768 bytes at 0x00000000 in 0.5 seconds (556.5 kbit/s)...
Hash of data verified.
Wrote 16384 bytes at 0x00008000 in 0.3 seconds (497.4 kbit/s)...
Hash of data verified.
Wrote 163840 bytes at 0x00010000 in 2.2 seconds (589.2 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
We are once again using the esptool write_flash
command and writing our three
binaries to flash in the same locations we did when building with esp-idf
. We
can use the west espressif monitor
command to reset the board and stream the
output, or we can connect with minicom
and use the RST button.
$ minicom -D /dev/ttyUSB0
ESP-ROM:esp32c3-api1-20210207
Build:Feb 7 2021
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:2
load:0x3fcd6100,len:0x15e0
load:0x403ce000,len:0x920
load:0x403d0000,len:0x2c4c
SHA-256 comparison failed:
Calculated: b77f73687acc559bca77cc4a4bea5409988930863749b80f171cc4a0501ff313
Expected: 318a48640313d299386e53a1953994f766ddfffed726be1320e4e027aa578799
Attempting to boot anyway...
entry 0x403ce000
I (49) boot: ESP-IDF 73e7af1 2nd stage bootloader
I (49) boot: compile time 22:29:26
I (49) boot: chip revision: 3
I (51) boot.esp32c3: SPI Speed : 40MHz
I (56) boot.esp32c3: SPI Mode : DIO
I (60) boot.esp32c3: SPI Flash Size : 4MB
I (65) boot: Enabling RNG early entropy source...
I (70) boot: Partition Table:
I (74) boot: ## Label Usage Type ST Offset Length
I (81) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (89) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (96) boot: 2 factory factory app 00 00 00010000 00100000
I (104) boot: End of partition table
I (108) esp_image: segment 0: paddr=00010020 vaddr=00000020 size=0001ch ( 28)
I (116) esp_image: segment 1: paddr=00010044 vaddr=3fc85020 size=000b8h ( 184) load
I (125) esp_image: segment 2: paddr=00010104 vaddr=3fc850d8 size=0023ch ( 572) load
I (133) esp_image: segment 3: paddr=00010348 vaddr=40380000 size=02c68h ( 11368) load
I (145) esp_image: segment 4: paddr=00012fb8 vaddr=00000000 size=0d080h ( 53376)
I (162) esp_image: segment 5: paddr=00020040 vaddr=3c000040 size=007d8h ( 2008) map
I (162) esp_image: segment 6: paddr=00020820 vaddr=00000000 size=0f7f8h ( 63480)
I (181) esp_image: segment 7: paddr=00030020 vaddr=42010020 size=04670h ( 18032) map
I (186) boot: Loaded app from partition at offset 0x10000
I (186) boot: Disabling RNG early entropy source...
�*** Booting Zephyr OS build 07c6af3b8c35 ***
Hello, RISC-V Bytes!
Concluding Thoughts Link to heading
Though Zephyr introduces its own flavors of tooling and workflow, we are still using the same underlying protocols and bootloader when working with ESP32 chips. It is not until we actually boot the application that we start to see where Zephyr and FreeRTOS diverge. We’ll look deeper into these differences in a future RISC-V Bytes post.
As always, these posts are meant to serve as a useful resource for folks who are interested in learning more about RISC-V and low-level software in general. If I can do a better job of reaching that goal, or you have any questions or comments, please feel free to send me a message @hasheddan on Twitter or @hasheddan@types.pl on Mastodon!