Skip to content
askarigtecPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

gcore

The core library of the g.tec BLE products, built on Zephyr and the nRF Connect SDK. It holds the BLE communication and the base functions every product shares, so a product's own application stays small.

Proprietary. For use within g.tec only. See LICENSE.md.

Status

gcore_run() turns Bluetooth on and advertises. Firmware updates come over BLE through MCUboot's firmware loader, which a phone resets the device into, see Bootloader and signing key. The rest of the BLE communication and the base functions come next.

Install

gcore is installed with zephyr-installer, 2.1.0 or newer. Run it in the application's folder, the one holding CMakeLists.txt and prj.conf:

pip install zephyr-installer
zephyr-installer askarigtec/lib

--ref v0.1.0 installs a release instead of main, once it is tagged. When the repository is private, the installer needs a GitHub token that can read it, in GITHUB_TOKEN. A local copy installs the same way: zephyr-installer C:/path/to/gcore.

The installer:

  • copies gcore's code into modules/gcore/ of the application, at the paths it has here: modules/gcore/src/gcore.h and modules/gcore/src/gcore.c,
  • writes modules/gcore/zephyr/, which makes gcore a Zephyr module: its module.yml, its CMakeLists.txt and its Kconfig,
  • adds modules/gcore to ZEPHYR_EXTRA_MODULES in CMakeLists.txt,
  • sets CONFIG_GCORE=y in prj.conf, with the release build's settings beside it, from requires.kconfig in installer.yml, see Build. The Bluetooth and MCUmgr symbols gcore needs are under zephyr.defaults there, which the installer writes into gcore's Kconfig as defaults: they hold while CONFIG_GCORE is on, and a line in the product's prj.conf that sets one of them wins,
  • writes the settings of MCUboot and its BLE firmware loader into sysbuild.conf,
  • copies VERSION, CHANGELOG.md, .clang-format, the board's overlay and .conf in boards/, prj_debug.conf, sysbuild_debug.conf, sysbuild.cmake, CMakePresets.json and sysbuild/mcuboot.conf into the application's folder, when they are not there yet,
  • copies gcore's tools into the application's tools/ folder, and writes them again on every install,
  • asks, then runs tools/keygen.py, which makes the product's own signing key, sign_key.pem. It is skipped when the product has one.

Each product names itself for phones with CONFIG_BT_DEVICE_NAME in its own prj.conf, below the installer's block.

keygen.py needs the cryptography package in the Python that runs the installer, see tools/keygen.md.

Running the same command again updates gcore. zephyr-installer --uninstall gcore takes it out, its tools/ included, and leaves the files it copied once and sign_key.pem, which are the product's.

Use

#include "gcore.h"

int main(void)
{
    return gcore_run();
}

Build

gcore is for the nRF54L15. A product has two builds, debug and release, each in a folder of its own. The release build has MCUboot and its BLE firmware loader, built with the application by sysbuild, as the product ships. The debug build has the application alone, from the start of the flash, so a debugger starts it and stops in it directly: MCUboot would start first, and check a signature a debugger's own image does not have. CMakePresets.json holds both for the IDE: in VS Code, in nRF Connect's Applications view, choose Add build configuration, pick Debug or Release under CMake Preset, check that the build folder is build-debug or build-release, and choose Generate and Build. Do it once for each, and both stay in the IDE from then on.

The release build is the plain one: prj.conf and sysbuild.conf are the release build's, and the debug build adds two files of its own, as Zephyr's own naming has it, the plain name for the usual build and _debug for the other:

Build Folder MCUboot Reads
release build-release yes prj.conf: the smallest code, with no log, printk, console, asserts or fault report, and a reset on a fatal error; sysbuild.conf: MCUboot and its loader
debug build-debug no the same, then prj_debug.conf: code a debugger follows (-Og) with every thread shown, asserts, a fatal error that stops for the debugger with its full report, and the log and printk over SWO, never the UART; and sysbuild_debug.conf in place of sysbuild.conf: no bootloader

The release settings come from gcore: requires.kconfig in installer.yml, which the installer writes into prj.conf, in gcore's block, on every install. In the product it reads:

# >>> zephyr-installer: gcore >>>
# Added by zephyr-installer, and written again on every install, so set
# gcore's settings below this block rather than in it.
CONFIG_GCORE=y
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_LOG=n
CONFIG_PRINTK=n
CONFIG_NCS_BOOT_BANNER=n
CONFIG_BOOT_BANNER=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
CONFIG_ASSERT=n
CONFIG_FAULT_DUMP=0
CONFIG_RESET_ON_FATAL_ERROR=y
# <<< zephyr-installer: gcore <<<

A product that wants one of them otherwise in its release build sets it below the block, and the installer leaves its own line out, commented.

prj_debug.conf is read after prj.conf, so its lines win: the log on, and debug optimization in place of size optimization, two of one choice, which Kconfig lets a later file change without a warning.

Each preset names every file its build reads, so nothing depends on what the IDE fills in by itself: prj.conf and boards/nrf54l15dk_nrf54l15_cpuapp.conf as its base configuration files, in that order, and boards/nrf54l15dk_nrf54l15_cpuapp.overlay, the product's peripherals and pins, as its devicetree overlay. The debug preset adds prj_debug.conf as its extra Kconfig fragment, EXTRA_CONF_FILE, and sysbuild_debug.conf in place of sysbuild.conf, SB_CONF_FILE: no MCUboot, no firmware loader and no signing. In the release build, sysbuild gives sysbuild/mcuboot.conf to MCUboot, which keeps MCUboot quiet. A product on a board of its own changes the board's name in both presets, BOARD and the two boards/ files.

A file in a preset starts with ${sourceDir}, the product's folder, as nRF Connect for VS Code writes presets itself: it takes a bare path such as prj.conf from its own folder, not the product's, and the build stops. SB_CONF_FILE is the one left bare, since sysbuild takes it from the product's folder itself.

prj_debug.conf is added to prj.conf, never put in its place, so it is an extra Kconfig fragment, not a file suffix, FILE_SUFFIX: in place of prj.conf it would leave out CONFIG_GCORE=y, which the installer writes there. sysbuild/mcuboot.conf is never named in a build configuration: named there, it goes to the application instead of MCUboot and stops its build.

The same two builds from the command line:

west build -b nrf54l15dk/nrf54l15/cpuapp -d build-release
west build -b nrf54l15dk/nrf54l15/cpuapp -d build-debug -- -DEXTRA_CONF_FILE=prj_debug.conf -DSB_CONF_FILE=sysbuild_debug.conf

There Zephyr finds the board's two files by their names, after prj.conf.

All debug output goes over SWO, the debug probe's trace pin: on the nRF54L15, P2.07 (TRACEDATA0), with P2.06 kept for the trace clock. On the nRF54L15 DK P2.07 is LED2 as well, so the debug build has no LED2. The SWO viewer, J-Link SWO Viewer or Ozone, is set to the speed prj_debug.conf gives, CONFIG_LOG_BACKEND_SWO_FREQ_HZ, 2 MHz.

After every release build, sysbuild.cmake copies the application's signed image, the one a phone sends to the firmware loader over BLE, into release/ in the product's folder, named after the application and the version in VERSION, such as release/test2-0.1.0.signed.bin, with the build number, VERSION_TWEAK, when it is not 0: release/test2-0.1.0+5.signed.bin, the version a phone shows for the image. The build number is raised by hand in VERSION, 0 to 255 as Zephyr takes it, and set to 0 again when the version itself goes up. VERSION is read at the end of every build, so a version changed since names the file too, and the application builds again with it by itself. Sysbuild reads sysbuild.cmake by its name. The debug build signs nothing, so it copies nothing.

The release build keeps the board's own partitions: MCUboot in boot_partition, the application in slot0_partition and the firmware loader in slot1_partition. The debug build, with no MCUboot, puts the application at the start of the flash, so flashing it replaces MCUboot and the loader on the device; flashing a release build puts them back.

Bootloader and signing key

Every product boots through MCUboot with a signing key of its own, so its bootloader refuses firmware built for another product. The installer writes MCUboot's settings into the application's sysbuild.conf, and tools/keygen.py makes the key they point at, see tools/keygen.md.

MCUboot runs in firmware loader mode. Beside the application, in its second slot, sits a small BLE firmware loader, NCS's ble_mcumgr, which takes the application over BLE. MCUboot boots the loader instead of the application in two cases:

  • The application asks for it. The application runs SMP over BLE for this one thing. A phone sends the OS group's reset command with boot mode 1 (BOOT_MODE_TYPE_BOOTLOADER); the application writes it into the boot mode byte and resets, and MCUboot clears the byte and boots the loader. The loader takes the new application and resets, and MCUboot boots it.
  • There is no application to boot, from sysbuild/mcuboot.conf. So a device programmed with MCUboot and the loader alone takes its application from a phone too, and so does one whose update did not finish.

The boot mode byte is the board's zephyr,boot-mode: on the nRF54L15 DK the first byte of GPREGRET1, which keeps its value through a reset. It is in the board's own devicetree, nrf54l_05_10_15_cpuapp_common.dtsi, so the application and MCUboot both find it with no overlay of gcore's. A board of a product's own that has none adds the same node to its devicetree.

The application itself takes no images: its own DFU would write into the second slot, over the loader. Its SMP is unauthenticated, so anyone in range can reset it into the loader, though the loader takes only images signed with the product's key. A product adds more ways into the loader in its sysbuild/mcuboot.conf, such as a button held at reset (CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO, with a mcuboot-button0 alias in MCUboot's devicetree), so that an application that no longer starts Bluetooth can still be replaced.

Project files

Every product keeps these in its own folder, next to CMakeLists.txt and prj.conf, where Zephyr reads them:

File What it is Where it comes from
VERSION the product's firmware version, which its images are signed with VERSION, copied once
CHANGELOG.md the product's changes, in the Keep a Changelog format templates/CHANGELOG.md, copied once
.clang-format the code style, for the product's code and gcore's alike .clang-format, copied once
boards/nrf54l15dk_nrf54l15_cpuapp.overlay the product's peripherals on its board, and their pins boards/, copied once
boards/nrf54l15dk_nrf54l15_cpuapp.conf the drivers of those peripherals boards/, copied once
prj_debug.conf the debug build's settings, on top of prj.conf, the release build's prj_debug.conf, copied once
sysbuild_debug.conf the debug build's sysbuild settings: no MCUboot sysbuild_debug.conf, copied once
sysbuild.cmake copies the release build's signed image to release/ sysbuild.cmake, copied once
release/ the signed image of the last release build, for an update over BLE written by every release build
CMakePresets.json the two builds, for the IDE to import CMakePresets.json, copied once
sysbuild/mcuboot.conf when MCUboot boots the firmware loader sysbuild/mcuboot.conf, copied once
sysbuild.conf the release build's: MCUboot, its firmware loader, and the key both are checked with a block written on every install, zephyr.sysbuild
tools/ gcore's tools, each with a page named after it, such as keygen.md tools/, written on every install
sign_key.pem the product's own signing key tools/keygen.py, run by the installer once

Each of these sits in this repository where it lands in the product, so the repository reads like a product's folder: VERSION, boards/, sysbuild/, tools/. The one exception is the product's CHANGELOG.md, which starts from templates/CHANGELOG.md, since CHANGELOG.md here is gcore's own.

A file copied once (once: in installer.yml) is the product's from then on: an update never replaces it, and uninstalling gcore leaves it, so a product changes it as it needs. A product on a board of its own renames the two files in boards/ after that board, such as bl54l15u_dvk_nrf54l15_cpuapp.overlay, since Zephyr finds them by the board's name. tools/ is gcore's, as modules/gcore/ is: written again on every install, so a change made to a tool there does not last, and taken out by an uninstall. The block in sysbuild.conf is gcore's and is written again on every install; the rest of that file is the product's.

installer.yml

installer.yml is what zephyr-installer reads. It asks for 2.1.0, so every path in it that names a place in the product starts at the product's folder, and every file is copied to the path it has here: a path alone is all an entry needs, and a to gives one another place. Each key does one thing:

Key What it does in the product
headers src/gcore.h into modules/gcore/src/, on the include path; the version is its @version
sources src/gcore.c into modules/gcore/src/, built while CONFIG_GCORE is on
extras LICENSE.md, README.md, CHANGELOG.md and doc/ into modules/gcore/; - root: tools into the product's own tools/ instead
once the product's own files, each at its own path, made when missing and never written over; CHANGELOG.md from templates/ with a to
run signing-key: runs the product's tools/keygen.py, unless sign_key.pem is there
requires.kconfig the release build's settings, set in prj.conf beside the switch, see Build
zephyr.kconfig the switch, CONFIG_GCORE, set in prj.conf
zephyr.defaults Bluetooth and MCUmgr, as configdefaults in gcore's Kconfig while CONFIG_GCORE is on
zephyr.sysbuild MCUboot, its firmware loader and the signing key, in sysbuild.conf

A to is another place where its list puts files. Under headers, sources and extras that is gcore's folder: - from: src/gcore.h, to: gcore.h lands at modules/gcore/gcore.h, the include path with it. Under once it is the product's folder, where every once file lands. A root puts an extra in the product's folder instead of gcore's, as tools/ is. A from that would be the same path as its to or root is left out. A wildcard, src/*.c or src/**/*.c, does as well as each file by name, matched the way a shell matches it.

CMake of its own

gcore needs no CMake beyond its files, so installer.yml has no zephyr.cmake. Lines a later version needs go there, under the place they go:

Place Written into For
module modules/gcore/zephyr/CMakeLists.txt, inside if(CONFIG_GCORE) gcore's own library, with zephyr_library_*
before-zephyr the product's CMakeLists.txt, above find_package(Zephyr) what Zephyr reads as it starts, such as EXTRA_CONF_FILE
after-zephyr the product's CMakeLists.txt, at its end what needs the product's own targets

A Kconfig fragment of gcore's own, for one, would reach the application this way, from gcore's folder:

zephyr:
  cmake:
    before-zephyr:
      - list(APPEND EXTRA_CONF_FILE "{library}/extra.conf")

The installer writes each place as one block between two lines that name it, gcore/<place>, and finds it by them on the next install: to update it, or take it out when gcore no longer has it, as it took out the before-zephyr block earlier versions had. {library} in a line is gcore's folder, wherever --dir put it, and ${CMAKE_CURRENT_SOURCE_DIR} CMake's own, the application's folder. Quote a line that holds a colon.

A product's own lines go outside these blocks, or between USER CODE BEGIN GCORE_CMAKE and USER CODE END GCORE_CMAKE in the module's file. Every install keeps them.

Layout

src/                 the library, gcore.h and gcore.c
doc/                 documentation, empty for now
installer.yml        what zephyr-installer installs, and how, gcore's Kconfig defaults included
CHANGELOG.md         gcore's own changes

Where they land in every product, copied once:
VERSION              the product's firmware version
prj_debug.conf       the debug build's settings, on top of prj.conf: the log over SWO
sysbuild_debug.conf  the debug build's sysbuild settings, in place of sysbuild.conf: no MCUboot
sysbuild.cmake       copies the release build's signed image to release/
CMakePresets.json    the two builds, for the IDE to import
boards/              the product's devicetree and settings for its board, the nRF54L15 DK
sysbuild/            MCUboot's settings: when it boots the firmware loader, and quiet
templates/           CHANGELOG.md, the product's own, started empty

Written into every product on every install:
tools/               gcore's tools, each with a page named after it

Version

The version is the @version tag in src/gcore.h and src/gcore.c, and nowhere else: zephyr-installer reads it from gcore.h. VERSION at the root is the product's, copied once into each product, not gcore's. Each release is tagged vX.Y.Z to match, and CHANGELOG.md lists what changed, in the Keep a Changelog format.

Code style

C11, Allman braces, four spaces, Doxygen comments, and every public name starting with gcore_ or GCORE_. .clang-format takes care of the layout. The installer copies it once into the product's folder, beside CMakeLists.txt, where clang-format finds it for the product's code and gcore's alike.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages