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.
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.
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.handmodules/gcore/src/gcore.c, - writes
modules/gcore/zephyr/, which makes gcore a Zephyr module: itsmodule.yml, itsCMakeLists.txtand itsKconfig, - adds
modules/gcoretoZEPHYR_EXTRA_MODULESinCMakeLists.txt, - sets
CONFIG_GCORE=yinprj.conf, with the release build's settings beside it, fromrequires.kconfigin installer.yml, see Build. The Bluetooth and MCUmgr symbols gcore needs are underzephyr.defaultsthere, which the installer writes into gcore's Kconfig as defaults: they hold whileCONFIG_GCOREis on, and a line in the product'sprj.confthat 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.confinboards/,prj_debug.conf,sysbuild_debug.conf,sysbuild.cmake,CMakePresets.jsonandsysbuild/mcuboot.confinto 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.
#include "gcore.h"
int main(void)
{
return gcore_run();
}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.confThere 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.
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.
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 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.
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.
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
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.
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.