Прочитайте статьи ...
"Zig On RISC-V BL602: быстрый взгляд с Apache Nuttx RTO"
"Создайте приложение IoT с Zig и Lorawan"
Чтобы построить приложение Hello Zig для Nuttx на BL602 ...
# # Enable Zig App in NuttX menuconfig
make menuconfig
# # TODO: Select "Application Configuration > Examples > Hello Zig Example"
# # Save the configuration and exit menuconfig.
# # Build Nuttx
make
# # NuttX Build fails with Undefined Reference to `hello_zig_main`
# # That's OK, here's the fix...
# # Download our modified Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu sifive_e76
hello_zig_main.zig
# # Dump the ABI for the compiled app
riscv64-unknown-elf-readelf -h -A hello_zig_main.o
# # Shows "Flags: 0x1, RVC, soft-float ABI"
# # Which is Software Floating-Point.
# # This won't link with NuttX because NuttX is compiled with Hardware Floating-Point
# # We change Software Floating-Point to Hardware Floating-Point...
# # Edit hello_zig_main.o in a Hex Editor, change byte 0x24 from 0x01 to 0x03
# # (See https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header)
# # Dump the ABI for the compiled app
riscv64-unknown-elf-readelf -h -A hello_zig_main.o
# # Shows "Flags: 0x3, RVC, single-float ABI"
# # Which is Hardware Floating-Point and will link with NuttX
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make
# # NuttX build should now succeedЗагрузите Nuttx и введите это в оболочке Nuttx ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
Для приложения Lorawan Zig, см. Это ...
Чтобы скомпилировать библиотеку Lora SX1262 в C с Zig Combiler, см. Это ...
Вот как мы заставили Зиг и Лораван бежать на BL602 Nuttx ...
Apache Nuttx RTOS в комплекте с простым приложением Zig ... давайте запустим это на BL602: hello_zig_main.zig
// Import the Zig Standard Library
const std = @import ( "std" );
// Import printf() from C
pub extern fn printf (
_format : [ * : 0 ] const u8
) c_int ;
// Main Function
pub export fn hello_zig_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}Мы исправили последние 2 строки, чтобы сделать Zig Compiler счастливым ...
// Previously: printf("Hello, Zig!n");
// Zig needs us to use the returned value from printf()...
_ = printf ( "Hello, Zig! n " );
// Previously this was missing.
// Zig needs us to return a value...
return 0 ;Оригинальная версия здесь: hello_zig_main.zig
Чтобы включить приложение Zig в Nuttx ...
make menuconfigВыберите «Конфигурация приложения> примеры> Hello Zig пример»
Сохраните конфигурацию и выходите из Menuconfig.
Когда мы строим Nuttx ...
makeМы видим эту ошибку ...
LD: nuttx
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xbc):
undefined reference to `hello_zig_main'
(Источник)
Что похоже на эту проблему ...
Apache/Nuttx#6219
Похоже, что это вызвано сборкой Nuttx, не вызывающей Zig Combiler.
Но не беспокойтесь! Давайте сами скомпилируем приложение Zig и связываем Nuttx.
Вот как мы скомпилируем наше приложение Zig для RISC-V BL602 и связываем его с Nuttx ...
# # Download our modified Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu sifive_e76
hello_zig_main.zig
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
make Почему целевой riscv32-freestanding-none ?
Целевые Zig имеют форму <arch><sub>-<os>-<abi> ...
riscv32 : Потому что BL602-32-битный процессор RISC-V
freestanding : потому что встроенные цели не нуждаются в ОС
none : потому что встроенные цели не указывают ABI
Почему целевой процессор sifive_e76 ?
BL602 обозначен как RV32IMACF ...
| Обозначение | Значение |
|---|---|
RV32I | 32-битный RISC-V с базовыми целочисленными инструкциями |
M | Целочисленное умножение + деление |
A | Атомные инструкции |
C | Сжатые инструкции |
F | Однопрекратное плавание |
(Источник)
Среди всех целей Zig только sifive_e76 имеет одинаковое обозначение ...
$ zig targets
...
" sifive_e76 " : [ " a " , " c " , " f " , " m " ],(Источник)
Таким образом, мы используем sifive_e76 в качестве нашей цели процессора.
В качестве альтернативы мы можем использовать baseline_rv32-d в качестве нашей цели процессора ...
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
hello_zig_main.zigПотому что...
baseline_rv32 означает rv32imacfd
(D для двойной режиссера)
-d означает удалить двойную рецепту с плавающей точкой (D)
(Но сохраните однооперацию плавающей точки)
(Подробнее о флагах функций RISC-V. Спасибо, Матема!)
При связывании скомпилированного приложения Zig с Nuttx мы видим эту ошибку ...
$ make
...
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(hello_main.c.home.user.nuttx.apps.examples.hello.o):
can ' t link soft-float modules with single-float modulesЭто потому, что nuttx был составлен для (одноприменения) аппаратного обеспечения с плавающей точкой ABI (двоичный интерфейс приложения) ...
# # Do this BEFORE overwriting hello.o by hello_zig_main.o.
# # "*hello.o" expands to something like "hello_main.c.home.user.nuttx.apps.examples.hello.o"
$ riscv64-unknown-elf-readelf -h -A $HOME /nuttx/apps/examples/hello/ * hello.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2 ' s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: RISC-V
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 4528 (bytes into file)
Flags: 0x3, RVC, single-float ABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 26
Section header string table index: 25
Attribute Section: riscv
File Attributes
Tag_RISCV_stack_align: 16-bytes
Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_c2p0"(Источник)
(Nuttx был составлен с флагами GCC -march=rv32imafc -mabi=ilp32f )
Принимая во внимание, что Zig Compiler создает объектный файл с программным обеспечением ABI ...
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2 ' s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: RISC-V
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 11968 (bytes into file)
Flags: 0x1, RVC, soft-float ABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 24
Section header string table index: 22
Attribute Section: riscv
File Attributes
Tag_RISCV_stack_align: 16-bytes
Tag_RISCV_arch: "rv32i2p0_m2p0_a2p0_f2p0_c2p0"(Источник)
GCC не позволит нам связывать объектные файлы с помощью программного обеспечения с плавающей запятой и аппаратным ABIS с плавающей запятой!
(Почему компилятор Zig создал объектный файл с программным обеспечением ABI, когда sifive_e76 поддерживает аппаратную точку с плавающей запятой? См. Это)
Zig Compiler генерирует объектный файл с программным обеспечением ABI с плавающей запятой (двоичный интерфейс приложения) ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABIЭто не будет связываться с Nuttx, потому что nuttx составлен с аппаратным обеспечением ABI.
Мы исправляем это, изменяя заголовок эльфа ...
Редактировать hello_zig_main.o в шестнадцатеричном редакторе
(Как vScode Hex Editor)
Измените байт 0x24 (флаги) с 0x01 (мягкий поплавок) на 0x03 (жесткий поплавок)
(См. Это)
Мы подтвердим, что объектный файл был изменен на аппаратное обеспечение с плавающей запятой ABI ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x3, RVC, single-float ABIТеперь это аппаратное обеспечение ABI с плавающей запятой и будет ссылаться с Nuttx.
Теперь мы связываем измененный файл объекта с Nuttx ...
# # Copy the compiled app to NuttX and overwrite `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp hello_zig_main.o $HOME /nuttx/apps/examples/hello/ * hello.o
# # Build NuttX to link the Zig Object from `hello.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeСборка Nuttx теперь должна добиться успеха.
Действительно ли можно изменить ABI, как это?
Ну, технически ABI правильно генерируется компилятором Zig ...
# # Dump the ABI for the compiled Zig app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABI
Tag_RISCV_arch: " rv32i2p0_m2p0_a2p0_f2p0_c2p0 "Последняя строка переводится как RV32IMACF , что означает, что набор инструкций RISC-V действительно предназначен для оборудования для плавающей точки .
Мы только редактируем заголовок ELF , потому что он, похоже, не отражает правильный ABI для объектного файла.
Есть ли правильное исправление для этого?
В будущем компилятор Zig может позволить нам указать ABI с плавающей точкой в качестве цели ...
# # Compile the Zig App for BL602
# # ("ilp32f" means Hardware Floating-Point ABI)
zig build-obj
-target riscv32-freestanding-ilp32f
...(См. Это)
Следите за обновлениями!
Можем ли мы исправить заголовок эльфа через командную строку?
Да, мы можем исправить заголовок эльфа через командную строку ...
xxd -c 1 hello_zig_main.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - hello_zig_main2.oСтроительство Nuttx добивается успеха. Zig работает ОК на Nuttx BL602!
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
Помните, что мы перезаписываем hello.o с помощью нашего скомпилированного объектного файла Zig.
Build Nuttx потерпит неудачу, если мы не предоставим функцию hello_main ...
riscv64-unknown-elf-ld: nuttx/staging/libapps.a(builtin_list.c.home.user.nuttx.apps.builtin.o):(.rodata.g_builtins+0xcc):
undefined reference to `hello_main'
Вот почему мы определяем hello_main в нашем приложении Zig ...
pub export fn hello_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}(Источник)
Это означает, что приложение hello также позвонит на наш Zig Code ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello
Hello, Zig!
Pine64 Pinecone BL602 Плата (справа), подключенная к Semtech SX1262 LORA Приемопередатчик (слева) над SPI
Будет ли компилятор Zig работать в качестве замены GCC для составления библиотек Nuttx?
Давайте проверим его в библиотеке Lora SX1262 для Apache Nuttx RTOS!
Вот как Nuttx собирает библиотеку Lora SX1262 с GCC ...
# # LoRa SX1262 Source Directory
cd $HOME /nuttx/nuttx/libs/libsx1262
# # Compile radio.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/radio.c
-o src/radio.o
# # Compile sx126x.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x.c
-o src/sx126x.o
# # Compile sx126x-nuttx.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x-nuttx.c
-o src/sx126x-nuttx.oМы вносим эти изменения ...
Изменить riscv64-unknown-elf-gcc на zig cc
Добавить Target -target riscv32-freestanding-none -mcpu=baseline_rv32-d
Удалить -march=rv32imafc
И мы запускаем это ...
# # LoRa SX1262 Source Directory
cd $HOME /nuttx/nuttx/libs/libsx1262
# # Compile radio.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/radio.c
-o src/radio.o
# # Compile sx126x.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x.c
-o src/sx126x.o
# # Compile sx126x-nuttx.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/sx126x-nuttx.c
-o src/sx126x-nuttx.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeZig Compiler показывает эти ошибки ...
In file included from src/sx126x-nuttx.c:3:
In file included from nuttx/include/debug.h:39:
In file included from nuttx/include/sys/uio.h:45:
nuttx/include/sys/types.h:119:9: error: unknown type name '_size_t'
typedef _size_t size_t;
^
nuttx/include/sys/types.h:120:9: error: unknown type name '_ssize_t'
typedef _ssize_t ssize_t;
^
nuttx/include/sys/types.h:121:9: error: unknown type name '_size_t'
typedef _size_t rsize_t;
^
nuttx/include/sys/types.h:174:9: error: unknown type name '_wchar_t'
typedef _wchar_t wchar_t;
^
In file included from src/sx126x-nuttx.c:4:
In file included from nuttx/include/stdio.h:34:
nuttx/include/nuttx/fs/fs.h:238:20: error: use of undeclared identifier 'NAME_MAX'
char parent[NAME_MAX + 1];
^
Мы исправляем это, включив правильные файлы заголовка ...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)В эти исходные файлы ...
(См. Изменения)
Мы вставляем этот код, чтобы сообщить нам (во время выполнения), был ли он составлен с Zig Compiler или GCC ...
void SX126xIoInit ( void ) {
#ifdef __clang__
#warning Compiled with zig cc
puts ( "SX126xIoInit: Compiled with zig cc" );
#else
#warning Compiled with gcc
puts ( "SX126xIoInit: Compiled with gcc" );
#endif // __clang__(Источник)
Скомпилированная с zig cc , библиотека Lora SX1262 работает OK OK на Nuttx Yay!
nsh> lorawan_test
SX126xIoInit: Compiled with zig cc
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 000E268C
DATA RATE : DR_2
...
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923400000
TX POWER : 0
CHANNEL MASK: 0003
(См. Полный журнал)
Давайте составим огромную библиотеку Лоравана с Zig Compiler.
Nuttx собирает библиотеку Lorawan, как это ...
# # LoRaWAN Source Directory
cd $HOME /nuttx/nuttx/libs/liblorawan
# # Compile mac/LoRaMac.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/mac/LoRaMac.c
-o src/mac/LoRaMac.oМы переключаемся на компилятор Zig ...
# # LoRaWAN Source Directory
cd $HOME /nuttx/nuttx/libs/liblorawan
# # Compile mac/LoRaMac.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe src/mac/LoRaMac.c
-o src/mac/LoRaMac.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeМы включаем правильные файлы заголовка в loramac.c ...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)(См. Изменения)
Loramac.c компилируется OK с Zig Compiler.
TODO: Скомпилируйте другие файлы в библиотеке Lorawan с build.zig
https://ziglang.org/documentation/master/#zig-build-system
TODO: Проверьте библиотеку Лоравана
Теперь мы скомпилируем приложение Lorawan Lorawan_test_main.c с помощью Zig Compiler.
Nuttx собирает приложение Lorawan lorawan_test_main.c
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test/lorawan_test_main.c
# # Compile lorawan_test_main.c with GCC
riscv64-unknown-elf-gcc
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-march=rv32imafc
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main lorawan_test_main.c
-o lorawan_test_main.c.home.user.nuttx.apps.examples.lorawan_test.oМы переключаемся на Zig Compiler ...
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test
# # Compile lorawan_test_main.c with zig cc
zig cc
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-c
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-pipe
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main lorawan_test_main.c
-o * lorawan_test.o
# # Link Zig Object Files with NuttX after compiling with `zig cc`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeМы включаем правильные файлы заголовка в lorawan_test_main.c ...
#if defined( __NuttX__ ) && defined( __clang__ ) // Workaround for NuttX with zig cc
#include <arch/types.h>
#include "../../nuttx/include/limits.h"
#endif // defined(__NuttX__) && defined(__clang__)(См. Изменения)
Приложение Lorawan, скомпилированное с zig cc , работает OK OK OK OK Nuttx Yay!
nsh> lorawan_test
lorawan_test_main: Compiled with zig cc
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 00DC5ED5
DATA RATE : DR_2
...
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923400000
TX POWER : 0
CHANNEL MASK: 0003
(См. Полный журнал)
Компилятор Zig может автоматически транслировать C код C до Zig. (См. Это)
Вот как мы автоматически транслируем наше приложение Lorawan lorawan_test_main.c от c до Zig ...
Изменить zig cc на zig translate-c
Окружите флаги C -cflags ... --
Так...
# # App Source Directory
cd $HOME /nuttx/apps/examples/lorawan_test
# # Auto-translate lorawan_test_main.c from C to Zig
zig translate-c
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-cflags
-fno-common
-Wall
-Wstrict-prototypes
-Wshadow
-Wundef
-Os
-fno-strict-aliasing
-fomit-frame-pointer
-fstack-protector-all
-ffunction-sections
-fdata-sections
-g
-mabi=ilp32f
-mno-relax
--
-isystem " $HOME /nuttx/nuttx/include "
-D__NuttX__
-DNDEBUG
-DARCH_RISCV
-I " $HOME /nuttx/apps/graphics/lvgl "
-I " $HOME /nuttx/apps/graphics/lvgl/lvgl "
-I " $HOME /nuttx/apps/include "
-Dmain=lorawan_test_main
lorawan_test_main.c
> lorawan_test_main.zigВот исходный код C: lorawan_test_main.c
И автоматическое переранлирование от C до Zig: перевод/lorawan_test_main.zig
Вот фрагмент из исходного кода C ...
int main ( int argc , FAR char * argv []) {
#ifdef __clang__
puts ( "lorawan_test_main: Compiled with zig cc" );
#else
puts ( "lorawan_test_main: Compiled with gcc" );
#endif // __clang__
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
init_entropy_pool ();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = APP_TX_DUTYCYCLE + randr ( - APP_TX_DUTYCYCLE_RND , APP_TX_DUTYCYCLE_RND );
const Version_t appVersion = { . Value = FIRMWARE_VERSION };
const Version_t gitHubVersion = { . Value = GITHUB_VERSION };
DisplayAppInfo ( "lorawan_test" ,
& appVersion ,
& gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
{
printf ( "LoRaMac wasn't properly initializedn" );
// Fatal error, endless loop.
while ( 1 ) {}
}
// Set system maximum tolerated rx error in milliseconds
LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
LmHandlerPackageRegister ( PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
LmHandlerPackageRegister ( PACKAGE_ID_CLOCK_SYNC , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_REMOTE_MCAST_SETUP , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
IsClockSynched = false;
IsFileTransferDone = false;
// Join the LoRaWAN Network
LmHandlerJoin ( );
// Set the Transmit Timer
StartTxProcess ( LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue ( NULL ); // Never returns
return 0 ;
}(Источник)
И автоматический трансляционный зиговый код ...
pub export fn lorawan_test_main ( arg_argc : c_int , arg_argv : [ * c ][ * c ] u8 ) c_int {
var argc = arg_argc ;
_ = argc ;
var argv = arg_argv ;
_ = argv ;
_ = puts ( "lorawan_test_main: Compiled with zig cc" );
init_entropy_pool ();
TxPeriodicity = @bitCast ( u32 , @as ( c_int , 40000 ) + randr ( - @as ( c_int , 5000 ), @as ( c_int , 5000 )));
const appVersion : Version_t = Version_t {
. Value = @bitCast ( u32 , @as ( c_int , 16908288 )),
};
const gitHubVersion : Version_t = Version_t {
. Value = @bitCast ( u32 , @as ( c_int , 83886080 )),
};
DisplayAppInfo ( "lorawan_test" , & appVersion , & gitHubVersion );
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS ) {
_ = printf ( "LoRaMac wasn't properly initialized n " );
while ( true ) {}
}
_ = LmHandlerSetSystemMaxRxError ( @bitCast ( u32 , @as ( c_int , 20 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 0 ))), @ptrCast ( ? * anyopaque , & LmhpComplianceParams ));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 1 ))), @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 2 ))), @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
_ = LmHandlerPackageRegister ( @bitCast ( u8 , @truncate ( i8 , @as ( c_int , 3 ))), @ptrCast ( ? * anyopaque , & FragmentationParams ));
IsClockSynched = @as ( c_int , 0 ) != 0 ;
IsFileTransferDone = @as ( c_int , 0 ) != 0 ;
LmHandlerJoin ();
StartTxProcess ( @bitCast ( c_uint , LORAMAC_HANDLER_TX_ON_TIMER ));
handle_event_queue ( @intToPtr ( ? * anyopaque , @as ( c_int , 0 )));
return 0 ;
}(Источник)
Мы обратимся к этому автоматическому Zig-коду, когда мы вручную конвертируем наше приложение Lorawan Lorawan_test_main.c из C в Zig в следующем разделе ...
Pine64 Pinineio Stack BL604 (слева) Говоря о Лораване до Раквалового Висгейта (справа)
Наконец, мы конвертируем приложение Lorawan Lorawan_test_main.c из C в Zig, чтобы показать, что мы можем создавать сложные приложения IoT в Zig.
Приложение Lorawan работает на стеке PINEDIO BL604 (RISC-V). Приложение подключается к шлюзу Lorawan (например, ChirpStack или The Things Network) и отправляет пакет данных через регулярные промежутки времени.
Вот исходный код C: lorawan_test_main.c
(700 строк кода C)
И наше преобразованное приложение Lorawan Zig: lorawan_test.zig
(673 строки Zig Code)
/// Import the LoRaWAN Library from C
const c = @cImport ({
// NuttX Defines
@cDefine ( "__NuttX__" , "" );
@cDefine ( "NDEBUG" , "" );
@cDefine ( "ARCH_RISCV" , "" );
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" );
@cDefine ( "__int_c_join(a, b)" , "a" ); // Bypass zig/lib/include/stdint.h
// NuttX Header Files
@cInclude ( "arch/types.h" );
@cInclude ( "../../nuttx/include/limits.h" );
@cInclude ( "stdio.h" );
// LoRaWAN Header Files
@cInclude ( "firmwareVersion.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/githubVersion.h" );
@cInclude ( "../libs/liblorawan/src/boards/utilities.h" );
@cInclude ( "../libs/liblorawan/src/mac/region/RegionCommon.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/Commissioning.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/LmHandler.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpCompliance.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpClockSync.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpRemoteMcastSetup.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandler/packages/LmhpFragmentation.h" );
@cInclude ( "../libs/liblorawan/src/apps/LoRaMac/common/LmHandlerMsgDisplay.h" );
});
// Main Function that will be called by NuttX
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
// Call the LoRaWAN Library to set system maximum tolerated rx error in milliseconds
_ = c . LmHandlerSetSystemMaxRxError ( 20 );
// TODO: Call the LoRaWAN Library to Join LoRaWAN Network
// and send a Data PacketЧтобы скомпилировать приложение Lorawan Zig Lorawan_test.zig ...
# # Download our LoRaWAN Zig App for NuttX
git clone --recursive https://github.com/lupyuen/zig-bl602-nuttx
cd zig-bl602-nuttx
# # Compile the Zig App for BL602 (RV32IMACF with Hardware Floating-Point)
zig build-obj
--verbose-cimport
-target riscv32-freestanding-none
-mcpu=baseline_rv32-d
-isystem " $HOME /nuttx/nuttx/include "
-I " $HOME /nuttx/apps/examples/lorawan_test "
lorawan_test.zig
# # Patch the ELF Header of `lorawan_test.o` from Soft-Float ABI to Hard-Float ABI
xxd -c 1 lorawan_test.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - lorawan_test2.o
cp lorawan_test2.o lorawan_test.o
# # Copy the compiled app to NuttX and overwrite `lorawan_test.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cp lorawan_test.o $HOME /nuttx/apps/examples/lorawan_test/ * lorawan_test.o
# # Build NuttX to link the Zig Object from `lorawan_test.o`
# # TODO: Change "$HOME/nuttx" to your NuttX Project Directory
cd $HOME /nuttx/nuttx
makeНаше приложение Lorawan Zig Lorawan_test.zig Компилируется OK с Zig Compiler после того, как сделает следующие исправления ...
Некоторые части приложения Lorawan Zig Lorawan_test.zig могут стать сложно конвертировать из C в Zig, как этот C -код ...
// Original C code...
#define APP_TX_DUTYCYCLE 40000
#define APP_TX_DUTYCYCLE_RND 5000
uint32_t TxPeriodicity =
APP_TX_DUTYCYCLE +
randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
);(Источник)
Который имеет конфликтующие типы подписанных ( randr ) и USIGNED ( APP_TX_DUTYCYCLE ).
Мы получаем помощь, ссылаясь на автоматический трансляционный Zig Code: перевод/lorawan_test_main.zig
// Converted from C to Zig...
const APP_TX_DUTYCYCLE : c_int = 40000 ;
const APP_TX_DUTYCYCLE_RND : c_int = 5000 ;
// Cast to u32 because randr() can be negative
var TxPeriodicity : u32 = @bitCast ( u32 ,
APP_TX_DUTYCYCLE +
c . randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
)
);Который разрешает противоречивые типы, отменив подписанный результат, чтобы стать без знака.
Когда мы ссылаемся на LmHandlerCallbacks в нашем приложении Lorawan Zig lorawan_test.zig ...
_ = & LmHandlerCallbacks ;Zig Compiler покажет эту непрозрачную ошибку типа ...
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1353:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions
Fields: struct_sInfoFields,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1563:5: note: while checking this field
PingSlot: PingSlotInfo_t,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1579:5: note: while checking this field
PingSlotInfo: MlmeReqPingSlotInfo_t,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:1585:5: note: while checking this field
Req: union_uMlmeParam,
^
zig-cache/o/d4d456612514c342a153a8d34fbf5970/cimport.zig:2277:5: note: while checking this field
OnMacMlmeRequest: ?fn (LoRaMacStatus_t, [*c]MlmeReq_t, TimerTime_t) callconv(.C) void,
^
Необеспеченная ошибка типа объясняется здесь ...
"Распространение проекта C/C ++ с Zig"
"Сбои перевода"
Давайте проследить через нашу непрозрачную ошибку типа ...
export fn OnMacMlmeRequest (
status : c.LoRaMacStatus_t ,
mlmeReq : [ * c ] c.MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void {
c . DisplayMacMlmeRequestUpdate ( status , mlmeReq , nextTxIn );
} Наша функция OnMacMlmeRequest имеет параметр типа MlmeReq_t , автоэимпортированный Zig Compiler как ...
pub const MlmeReq_t = struct_sMlmeReq ;
pub const struct_sMlmeReq = extern struct {
Type : Mlme_t ,
Req : union_uMlmeParam ,
ReqReturn : RequestReturnParam_t ,
}; Который содержит еще один автозамененный тип union_uMlmeParam ...
pub const union_uMlmeParam = extern union {
Join : MlmeReqJoin_t ,
TxCw : MlmeReqTxCw_t ,
PingSlotInfo : MlmeReqPingSlotInfo_t ,
DeriveMcKEKey : MlmeReqDeriveMcKEKey_t ,
DeriveMcSessionKeyPair : MlmeReqDeriveMcSessionKeyPair_t ,
}; Который содержит MlmeReqPingSlotInfo_t ...
pub const MlmeReqPingSlotInfo_t = struct_sMlmeReqPingSlotInfo ;
pub const struct_sMlmeReqPingSlotInfo = extern struct {
PingSlot : PingSlotInfo_t ,
}; Который содержит PingSlotInfo_t ...
pub const PingSlotInfo_t = union_uPingSlotInfo ;
pub const union_uPingSlotInfo = extern union {
Value : u8 ,
Fields : struct_sInfoFields ,
}; Который содержит struct_sInfoFields ...
pub const struct_sInfoFields = opaque {}; Но поля struct_sInfoFields не известны Zig Compiler!
Если мы обратимся к исходному коду C ...
typedef union uPingSlotInfo
{
/*!
* Parameter for byte access
*/
uint8_t Value ;
/*!
* Structure containing the parameters for the PingSlotInfoReq
*/
struct sInfoFields
{
/*!
* Periodicity = 0: ping slot every second
* Periodicity = 7: ping slot every 128 seconds
*/
uint8_t Periodicity : 3 ;
/*!
* RFU
*/
uint8_t RFU : 5 ;
} Fields ;
} PingSlotInfo_t ;(Источник)
Мы видим, что sInfoFields содержит битовые поля, что компилятор Zig не может перевести.
Ранее мы видели, что это не может компилироваться в нашем приложении Lorawan Zig Lorawan_test.zig ...
_ = & LmHandlerCallbacks ; Это связано с тем, что LmHandlerCallbacks ссылается на автозаимпортированный тип MlmeReq_t , который содержит битовые поля и не может быть переведен компилятором Zig.
Давайте преобразуем MlmeReq_t в непрозрачный тип, так как мы все равно не будем получать доступ к полям ...
/// We use an Opaque Type to represent MLME Request, because it contains Bit Fields that can't be converted by Zig
const MlmeReq_t = opaque {};(Источник)
Мы конвертируем LmHandlerCallbacks , чтобы использовать наш непрозрачный тип MlmeReq_t ...
/// Handler Callbacks. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2818-L2833
pub const LmHandlerCallbacks_t = extern struct {
GetBatteryLevel : ? fn () callconv ( .C ) u8 ,
GetTemperature : ? fn () callconv ( .C ) f32 ,
GetRandomSeed : ? fn () callconv ( .C ) u32 ,
OnMacProcess : ? fn () callconv ( .C ) void ,
OnNvmDataChange : ? fn ( c.LmHandlerNvmContextStates_t , u16 ) callconv ( .C ) void ,
OnNetworkParametersChange : ? fn ([ * c ] c.CommissioningParams_t ) callconv ( .C ) void ,
OnMacMcpsRequest : ? fn ( c.LoRaMacStatus_t , [ * c ] c.McpsReq_t , c.TimerTime_t ) callconv ( .C ) void ,
/// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`
OnMacMlmeRequest : ? fn ( c.LoRaMacStatus_t , * MlmeReq_t , c.TimerTime_t ) callconv ( .C ) void ,
OnJoinRequest : ? fn ([ * c ] c.LmHandlerJoinParams_t ) callconv ( .C ) void ,
OnTxData : ? fn ([ * c ] c.LmHandlerTxParams_t ) callconv ( .C ) void ,
OnRxData : ? fn ([ * c ] c.LmHandlerAppData_t , [ * c ] c.LmHandlerRxParams_t ) callconv ( .C ) void ,
OnClassChange : ? fn ( c.DeviceClass_t ) callconv ( .C ) void ,
OnBeaconStatusChange : ? fn ([ * c ] c.LoRaMacHandlerBeaconParams_t ) callconv ( .C ) void ,
OnSysTimeUpdate : ? fn ( bool , i32 ) callconv ( .C ) void ,
};(Источник)
Мы меняем все автоматические ссылки MlmeReq_t из ...
[ * c ] c . MlmeReq_tНа наш непрозрачный тип ...
* MlmeReq_t Мы также изменяем все автозавитчивые ссылки LmHandlerCallbacks_t из ...
[ * c ] c . LmHandlerCallbacks_t Нашим преобразованным LmHandlerCallbacks_t ...
* LmHandlerCallbacks_tЭто означает, что нам нужно импортировать затронутые функции Лоравана сами ...
/// Changed `[*c]c.MlmeReq_t` to `*MlmeReq_t`. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2905
extern fn DisplayMacMlmeRequestUpdate (
status : c.LoRaMacStatus_t ,
mlmeReq : * MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void ;
/// Changed `[*c]c.LmHandlerCallbacks_t` to `*LmHandlerCallbacks_t`. Adapted from
/// https://github.com/lupyuen/zig-bl602-nuttx/blob/main/translated/lorawan_test_main.zig#L2835
extern fn LmHandlerInit (
callbacks : * LmHandlerCallbacks_t ,
handlerParams : [ * c ] c . LmHandlerParams_t
) c.LmHandlerErrorStatus_t ;(Источник)
После исправления непрозрачного типа, Zig Compiler успешно компилирует наше приложение Lorawan Test Lorawan_test.zig Yay!
При составлении нашего тестового приложения Lorawan lorawan_test.zig мы видим эту ошибку макроса ...
zig-cache/o/23409ceec9a6e6769c416fde1695882f/cimport.zig:2904:32:
error: unable to translate macro: undefined identifier `LL`
pub const __INT64_C_SUFFIX__ = @compileError("unable to translate macro: undefined identifier `LL`");
// (no file):178:9
Согласно Docs Zig, это означает, что компилятор Zig не удалось перевести макрос C -C ...
Итак, мы определяем LL ...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" ); ( LL - это суффикс «длинный длинный» для констант C, который, вероятно, не требуется, когда мы импортируем типы C и функции в Zig)
Затем Zig Compileer издает эту ошибку ...
zig-cache/o/83fc6cf7a78f5781f258f156f891554b/cimport.zig:2940:26:
error: unable to translate C expr: unexpected token '##'
pub const __int_c_join = @compileError("unable to translate C expr: unexpected token '##'");
// /home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/include/stdint.h:282:9
Который относится к этой линии в stdint.h ...
#define __int_c_join ( a , b ) a ## b Макро __int_c_join не сбои, потому что суффикс LL теперь пуст, а оператор ## Concatenation выходит из строя.
Мы переопределяем макрос __int_c_join без оператора ## concatenation ...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" );
@cDefine ( "__int_c_join(a, b)" , "a" ); // Bypass zig/lib/include/stdint.hТеперь Zig Compiler успешно компилирует наше тестовое приложение Lorawan lorawan_test.zig
Компилятор Zig вылетает, когда он пытается инициализировать структуру таймера при запуске ...
/// Timer to handle the application data transmission duty cycle
var TxTimer : c.TimerEvent_t =
std . mem . zeroes ( c . TimerEvent_t );
// Zig Compiler crashes with...
// TODO buf_write_value_bytes maybe typethread 11512 panic:
// Unable to dump stack trace: debug info stripped(Источник)
Таким образом, вместо этого мы инициализируем структуру таймера в основной функции ...
/// Timer to handle the application data transmission duty cycle.
/// Init the timer in Main Function.
var TxTimer : c.TimerEvent_t = undefined ;
/// Main Function
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
// Init the Timer Struct at startup
TxTimer = std . mem . zeroes ( c . TimerEvent_t );(Источник)
После исправления вышеуказанных проблем мы проверяем приложение Lorawan Zig на Nuttx: lorawan_test.zig
nsh> lorawan_test
Application name : Zig LoRaWAN Test
...
###### =========== MLME-Confirm ============ ######
STATUS : OK
###### =========== JOINED ============ ######
OTAA
DevAddr : 00D803AB
DATA RATE : DR_2
...
PrepareTxFrame: Transmit to LoRaWAN (9 bytes): Hi NuttX
###### =========== MCPS-Confirm ============ ######
STATUS : OK
###### ===== UPLINK FRAME 1 ===== ######
CLASS : A
TX PORT : 1
TX DATA : UNCONFIRMED
48 69 20 4E 75 74 74 58 00
DATA RATE : DR_3
U/L FREQ : 923200000
TX POWER : 0
CHANNEL MASK: 0003
(См. Полный журнал)
Приложение Lorawan Zig Lorawan_test.zig успешно присоединяется к сети Lorawan (Chirpstack на Rakwireless Wisgate) и отправляет пакет данных в шлюз Lorawan Gateway Yay!
Компилятор Zig показывает интересную информацию при автоматической трансрантации нашего C-кода в Zig.
Этот код C копирует массив, байт по байту ...
static int8_t FragDecoderWrite ( uint32_t addr , uint8_t * data , uint32_t size ) {
...
for ( uint32_t i = 0 ; i < size ; i ++ ) {
UnfragmentedData [ addr + i ] = data [ i ];
}
return 0 ; // Success
}(Источник)
Вот автоматический трансляционный зиговый код ...
pub fn FragDecoderWrite ( arg_addr : u32 , arg_data : [ * c ] u8 , arg_size : u32 ) callconv ( .C ) i8 {
...
var size = arg_size ;
var i : u32 = 0 ;
while ( i < size ) : ( i +%= 1 ) {
UnfragmentedData [ addr +% i ] = data [ i ];
}
return 0 ;
}(Источник)
Обратите внимание, что индексация массива в C ...
// Array Indexing in C...
UnfragmentedData [ addr + i ]Переводится на это в Zig ...
// Array Indexing in Zig...
UnfragmentedData [ addr +% i ] + В C становится +% в Zig!
Что такое +% в Zig?
Это оператор Zig для дополнения .
Это означает, что результат возвращается к 0 (и дальше), если добавление переполняет целое число.
Но это не то, что мы задумали, так как мы не ожидаем дополнения к переполнению. Вот почему в нашем окончательном конвертированном Zig Code мы возвращаем +% назад к + ...
export fn FragDecoderWrite ( addr : u32 , data : [ * c ] u8 , size : u32 ) i8 {
...
var i : u32 = 0 ;
while ( i < size ) : ( i += 1 ) {
UnfragmentedData [ addr + i ] = data [ i ];
}
return 0 ; // Success
}(Источник)
Что произойдет, если дополнение переполнится?
Мы увидим ошибку во время выполнения ...
panic: integer overflow
(Источник)
Что, вероятно, хорошо, чтобы убедиться, что наши ценности разумны.
Что если наш индекс массива выходит за пределы границ?
Мы получим эту ошибку во время выполнения ...
panic: index out of bounds
(Источник)
Вот список проверки безопасности , проведенные Zig во время выполнения ...
Если мы предпочитаем жить безрассудно, это то, как мы отключаем проверки безопасности ...
Некоторые функции отладки, кажется, не работают? Как unreachable , std.debug.assert и std.debug.panic ?
Это потому, что для встроенных платформ нам нужно реализовать наш собственный обработчик паники ...
«Использование Zig для обеспечения трассов стека на панике ядра для операционной системы с голой кости»
Управление паникой по умолчанию: std.debug.default_panic
С помощью нашего собственного обработчика паники, эта неудача в утверждении ...
// Create a short alias named `assert`
const assert = std . debug . assert ;
// Assertion Failure
assert ( TxPeriodicity != 0 );Покажет этот след стека ...
!ZIG PANIC!
reached unreachable code
Stack Trace:
0x23016394
0x23016ce0
Согласно нашей разборке RISC-V, первый адрес 23016394 не выглядит интересным, потому что он находится внутри функции assert ...
/home/user/zig-linux-x86_64-0.10.0-dev.2351+b64a1d5ab/lib/std/debug.zig:259
pub fn assert(ok: bool) void {
2301637c: 00b51c63 bne a0,a1,23016394 <std.debug.assert+0x2c>
23016380: a009 j 23016382 <std.debug.assert+0x1a>
23016382: 2307e537 lui a0,0x2307e
23016386: f9850513 addi a0,a0,-104 # 2307df98 <__unnamed_4>
2301638a: 4581 li a1,0
2301638c: 00000097 auipc ra,0x0
23016390: f3c080e7 jalr -196(ra) # 230162c8 <panic>
if (!ok) unreachable; // assertion failure
23016394: a009 j 23016396 <std.debug.assert+0x2e>
Но второй адрес 23016ce0 раскрывает утверждение, которое не удалось ...
/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:95
assert(TxPeriodicity != 0);
23016ccc: 42013537 lui a0,0x42013
23016cd0: fbc52503 lw a0,-68(a0) # 42012fbc <TxPeriodicity>
23016cd4: 00a03533 snez a0,a0
23016cd8: fffff097 auipc ra,0xfffff
23016cdc: 690080e7 jalr 1680(ra) # 23016368 <std.debug.assert>
/home/user/nuttx/zig-bl602-nuttx/lorawan_test.zig:100
TxTimer = std.mem.zeroes(c.TimerEvent_t);
23016ce0: 42016537 lui a0,0x42016
Это наша реализация обработчика Zig Panic ...
/// Called by Zig when it hits a Panic. We print the Panic Message, Stack Trace and halt. See
/// https://andrewkelley.me/post/zig-stack-traces-kernel-panic-bare-bones-os.html
/// https://github.com/ziglang/zig/blob/master/lib/std/builtin.zig#L763-L847
pub fn panic (
message : [] const u8 ,
_stack_trace : ? * std . builtin . StackTrace
) noreturn {
// Print the Panic Message
_ = _stack_trace ;
_ = puts ( " n !ZIG PANIC!" );
_ = puts ( @ptrCast ([ * c ] const u8 , message ));
// Print the Stack Trace
_ = puts ( "Stack Trace:" );
var it = std . debug . StackIterator . init ( @returnAddress (), null );
while ( it . next ()) | return_address | {
_ = printf ( "%p n " , return_address );
}
// Halt
while ( true ) {}
}(Источник)
Мы внедрили отладочную регистрацию std.log.debug , который описан здесь ...
Вот как мы называем std.log.debug , чтобы распечатать сообщение журнала ...
// Create a short alias named `debug`
const debug = std . log . debug ;
// Message with 8 bytes
const msg : [] const u8 = "Hi NuttX" ;
// Print the message
debug ( "Transmit to LoRaWAN ({} bytes): {s}" , .{
msg . len , msg
});
// Prints: Transmit to LoRaWAN (8 bytes): Hi NuttX .{ ... } создает анонимную структуру с переменным количеством аргументов, которые будут переданы std.log.debug для печати.
Ниже приведена наша реализация std.log.debug ...
/// Called by Zig for `std.log.debug`, `std.log.info`, `std.log.err`, ...
/// https://gist.github.com/leecannon/d6f5d7e5af5881c466161270347ce84d
pub fn log (
comptime _message_level : std.log.Level ,
comptime _scope : @Type ( .EnumLiteral ),
comptime format : [] const u8 ,
args : anytype ,
) void {
_ = _message_level ;
_ = _scope ;
// Format the message
var buf : [ 100 ] u8 = undefined ; // Limit to 100 chars
var slice = std . fmt . bufPrint ( & buf , format , args )
catch { _ = puts ( "*** log error: buf too small" ); return ; };
// Terminate the formatted message with a null
var buf2 : [ buf . len + 1 : 0 ] u8 = undefined ;
std . mem . copy (
u8 ,
buf2 [0 .. slice . len ],
slice [0 .. slice . len ]
);
buf2 [ slice . len ] = 0 ;
// Print the formatted message
_ = puts ( & buf2 );
}(Источник)
Эта реализация вызовов puts() , которая поддерживается Apache Nuttx RTOS, так как она соответствует POSIX .
Оригинальный C -код и конвертированный Zig Code для нашего приложения Lorawan выглядят очень похожими.
Вот основная функция из нашего исходного кода C ...
/// Main Function that will be called by NuttX. We call the LoRaWAN Library
/// to Join a LoRaWAN Network and send a Data Packet.
int main ( int argc , FAR char * argv []) {
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
init_entropy_pool ();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = APP_TX_DUTYCYCLE + randr ( - APP_TX_DUTYCYCLE_RND , APP_TX_DUTYCYCLE_RND );
const Version_t appVersion = { . Value = FIRMWARE_VERSION };
const Version_t gitHubVersion = { . Value = GITHUB_VERSION };
DisplayAppInfo ( "lorawan_test" ,
& appVersion ,
& gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams ) != LORAMAC_HANDLER_SUCCESS )
{
printf ( "LoRaMac wasn't properly initializedn" );
// Fatal error, endless loop.
while ( 1 ) {}
}
// Set system maximum tolerated rx error in milliseconds
LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
LmHandlerPackageRegister ( PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
LmHandlerPackageRegister ( PACKAGE_ID_CLOCK_SYNC , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_REMOTE_MCAST_SETUP , NULL );
LmHandlerPackageRegister ( PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
IsClockSynched = false;
IsFileTransferDone = false;
// Join the LoRaWAN Network
LmHandlerJoin ( );
// Set the Transmit Timer
StartTxProcess ( LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue ( NULL ); // Never returns
return 0 ;
}(Источник)
И основная функция из нашего конвертированного Zig Code (после некоторой очистки) ...
/// Main Function that will be called by NuttX. We call the LoRaWAN Library
/// to Join a LoRaWAN Network and send a Data Packet.
pub export fn lorawan_test_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
// Init the Timer Struct at startup
TxTimer = std . mem . zeroes ( c . TimerEvent_t );
// If we are using Entropy Pool and the BL602 ADC is available,
// add the Internal Temperature Sensor data to the Entropy Pool
// TODO: init_entropy_pool();
// Compute the interval between transmissions based on Duty Cycle
TxPeriodicity = @bitCast ( u32 , // Cast to u32 because randr() can be negative
APP_TX_DUTYCYCLE +
c . randr (
- APP_TX_DUTYCYCLE_RND ,
APP_TX_DUTYCYCLE_RND
)
);
// Show the Firmware and GitHub Versions
const appVersion = c.Version_t {
. Value = c . FIRMWARE_VERSION ,
};
const gitHubVersion = c.Version_t {
. Value = c . GITHUB_VERSION ,
};
c . DisplayAppInfo ( "Zig LoRaWAN Test" , & appVersion , & gitHubVersion );
// Init LoRaWAN
if ( LmHandlerInit ( & LmHandlerCallbacks , & LmHandlerParams )
!= c . LORAMAC_HANDLER_SUCCESS ) {
std . log . err ( "LoRaMac wasn't properly initialized" , .{});
// Fatal error, endless loop.
while ( true ) {}
}
// Set system maximum tolerated rx error in milliseconds
_ = c . LmHandlerSetSystemMaxRxError ( 20 );
// The LoRa-Alliance Compliance protocol package should always be initialized and activated.
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_COMPLIANCE , & LmhpComplianceParams );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_CLOCK_SYNC , null );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_REMOTE_MCAST_SETUP , null );
_ = c . LmHandlerPackageRegister ( c . PACKAGE_ID_FRAGMENTATION , & FragmentationParams );
// Init the Clock Sync and File Transfer status
IsClockSynched = false ;
IsFileTransferDone = false ;
// Join the LoRaWAN Network
c . LmHandlerJoin ();
// Set the Transmit Timer
StartTxProcess ( LmHandlerTxEvents_t . LORAMAC_HANDLER_TX_ON_TIMER );
// Handle LoRaWAN Events
handle_event_queue (); // Never returns
return 0 ;
}(Источник)
TODO: Очистите названия типов, функций и переменных
TODO: Прочитайте внутренний датчик температуры
TODO: кодируйте данные датчика температуры с помощью TinyCbor и передавать в сеть вещей
https://lupyuen.github.io/articles/cbor2
TODO: Мониторинг данных датчиков с Prometheus и Grafana
https://lupyuen.github.io/articles/prometheus
TODO: добавьте новый код с @import()
https://zig.news/mattnite/import-and-packages-23mb
TODO: Нужно ли нам выравнивать буферы до 32 бит при экспорте в C?
/// User application data
/// (Aligned to 32-bit because it's exported to C)
var AppDataBuffer : [ LORAWAN_APP_DATA_BUFFER_MAX_SIZE ] u8 align ( 4 ) =
std . mem . zeroes ([ LORAWAN_APP_DATA_BUFFER_MAX_SIZE ] u8 );Отражение типа Zig ... Можем ли мы использовать его для создания структурированного графа вызовов для библиотек C ... как для библиотеки Lorawan? ?
Эта Zig Program импортирует библиотеку Lorawan из C и выпускает типы и функции Lorawan ...
// Do Type Reflection on the imported C functions
fn reflect () void {
// We run this at Compile-Time (instead of Runtime)...
comptime {
// Allow Zig Compiler to loop up to 100,000 times (Default is 1,000)
@setEvalBranchQuota ( 100_000 );
// Get the Type Info of the C Namespace
const T = @typeInfo ( c );
// Show the Type Info of the C Namespace (Struct)
@compileLog ( "@typeInfo(c): " , T );
// Shows | *"@typeInfo(c): ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
// Show the number of Fields in the C Namespace (0)
@compileLog ( "T.Struct.fields.len: " , T . Struct . fields . len );
// Shows | *"T.Struct.fields.len: ", 0
// Show the number of Declarations in the C Namespace (4743)
@compileLog ( "T.Struct.decls.len: " , T . Struct . decls . len );
// Shows | *"T.Struct.decls.len: ", 4743
// Show the first Declaration in the C Namespace (__builtin_bswap16)
@compileLog ( "T.Struct.decls[0].name: " , T . Struct . decls [ 0 ]. name );
// Shows | *"T.Struct.decls[0].name: ", "__builtin_bswap16"
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) {
// Dump the C Declaration
var T2 = @typeInfo ( c );
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );
// Strangely we can't do this...
// @compileLog("decl.name: ", decl.name);
// Because it shows...
// *"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
}
}
} // End of Compile-Time Code
}(Источник)
( @typeInfo объясняется здесь)
Когда компилятор Zig собирает приведенный выше код, мы видим это во время компиляции ...
$ zig build-obj --verbose-cimport -target riscv32-freestanding-none -mcpu=baseline_rv32-d -isystem /Users/Luppy/pinecone/nuttx/nuttx/include -I /Users/Luppy/pinecone/nuttx/apps/examples/lorawan_test reflect.zig
info(compilation): C import output: zig-cache/o/e979b806463a36dcecc2ef773bd2d2ad/cimport.zig
| *"@typeInfo(c): ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
| *"T.Struct.fields.len: ", 0
| *"T.Struct.decls.len: ", 4744
| *"T.Struct.decls[0].name: ", "__builtin_bswap16"
| *"decl.name: ", "LmnStatus_t"
| *"decl.name: ", "LmHandlerAdrStates_t"
| *"decl.name: ", "LmHandlerFlagStatus_t"
...
| *"decl.name: ", "LmHandlerInit"
| *"decl.name: ", "LmHandlerIsBusy"
| *"decl.name: ", "LmHandlerProcess"
| *"decl.name: ", "LmHandlerGetDutyCycleWaitTime"
| *"decl.name: ", "LmHandlerSend"
| *"decl.name: ", "LmHandlerJoin"
...
./reflect.zig:836:9: error: found compile log statement
@compileLog("@typeInfo(c): ", T);
^
./reflect.zig:840:9: error: found compile log statement
@compileLog("T.Struct.fields.len: ", T.Struct.fields.len);
^
./reflect.zig:844:9: error: found compile log statement
@compileLog("T.Struct.decls.len: ", T.Struct.decls.len);
^
./reflect.zig:848:9: error: found compile log statement
@compileLog("T.Struct.decls[0].name: ", T.Struct.decls[0].name);
^
./reflect.zig:857:17: error: found compile log statement
@compileLog("decl.name: ", T2.Struct.decls[i].name);
^
(Источник)
Который представляет собой список типов C и функций из библиотеки Лоравана.
Давайте использовать это для визуализации графа вызовов (по модулю) для библиотеки Лоравана.
Мы отобразим график вызовов с помощью Mermaid.js ...
https://mermaid-js.github.io/mermaid/#/./flowchart?id=flowcharts
И мы будем сгруппировать функции Lorawan в графике вызовов от Lorawan Module (подграф), чтобы мы могли увидеть вызовы в модулях Lorawan.
В decl.name выше коде, почему мы использовали T2.Struct.decls[i].name .
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) {
// Dump the C Declaration
var T2 = @typeInfo ( c );
// Can't use decl.name here...
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );
}
}Мы ожидаем, что этот код напечатает имя декларации ...
@compileLog ( "decl.name: " , decl . name );Как так ...
"decl.name: ", "LmnStatus_t"
Но, как ни странно, это печатает байты ...
"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
Zig Compiler, кажется, интерпретирует имя по -разному после того, как мы ссылались на имя ранее ...
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) { ...Итак, вместо этого мы используем этот обходной путь ...
// Get a fresh reference to the Type Info
var T2 = @typeInfo ( c );
// This works OK
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );Который дает результат, который нам нужен ...
"decl.name: ", "LmnStatus_t"
Можем ли мы автоматически идентифицировать модуль Lorawan для каждой функции Lorawan, проанализировав макросы заголовка HEADER_NAME_H__ C?
Давайте попробуем это, чтобы выбросить все объявления C и макросы, импортированные из C ...
// Get the Type Info of the C Namespace
const T = @typeInfo ( c );
// Remember the C Header
var header : [] const u8 = "" ;
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
var T2 = @typeInfo ( c );
// If the C Declaration ends with "_H"...
if (
std . mem . endsWith ( u8 , decl . name , "_H" ) or
std . mem . endsWith ( u8 , decl . name , "_H_" ) or
std . mem . endsWith ( u8 , decl . name , "_H__" )
) {
// Dump the C Header and remember it
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "-----" , name );
header = name ;
} else {
// Dump the C Declaration
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "decl.name:" , name );
}
} // End of C Declaration(Источник)
Мы получаем этот список функций Лоравана и макросов Лоравана ...
| *"decl.name:", "LmHandlerInit"
| *"decl.name:", "LmHandlerIsBusy"
| *"decl.name:", "LmHandlerProcess"
| *"decl.name:", "LmHandlerGetDutyCycleWaitTime"
| *"decl.name:", "LmHandlerSend"
| *"decl.name:", "LmHandlerJoin"
...
| *"-----", "__LORAMAC_HANDLER_H__"
| *"-----", "__LORAMAC_HANDLER_TYPES_H__"
| *"decl.name:", "LMH_SYS_TIME_UPDATE_NEW_API"
| *"decl.name:", "__LMHP_COMPLIANCE__"
(Источник)
Что бесполезно. LmHandlerInit фактически объявлен в файле заголовка C для __LORAMAC_HANDLER_H__ . Но каким -то образом отражение типа Zig перемещает LmHandlerInit к вершине, прежде чем появляется __LORAMAC_HANDLER_H__ .
Таким образом, кажется, что нам нужно вручную группировать функции Lorawan в модули Lorawan.
Функции Lorawan, по -видимому, секвенированы в соответствии с файлом заголовка C, поэтому нам нужно только вручную пометить первую функцию Lorawan для каждого модуля Lorawan. Так...
LmHandlerInit → __LORAMAC_HANDLER_H__
LoRaMacInitialization → __LORAMAC_H__
RegionCommonValueInRange → __REGIONCOMMON_H__
SX126xInit → __SX126x_H__
SX126xIoInit → __SX126x_BOARD_H__
Наше приложение Zig может делать отражение типа, чтобы обнаружить его типы, константы, переменные и функции ...
// Show the Type Info for our Zig Namespace
const ThisType = @typeInfo ( @This ());
@compileLog ( "ThisType: " , ThisType );
@compileLog ( "ThisType.Struct.decls.len: " , ThisType . Struct . decls . len );
@compileLog ( "ThisType.Struct.decls[0].name: " , ThisType . Struct . decls [ 0 ]. name );
@compileLog ( "ThisType.Struct.decls[1].name: " , ThisType . Struct . decls [ 1 ]. name );
@compileLog ( "ThisType.Struct.decls[2].name: " , ThisType . Struct . decls [ 2 ]. name );
// Shows...
// | *"ThisType: ", std.builtin.Type { .Struct = (struct std.builtin.Type.Struct constant)}
// | *"ThisType.Struct.decls.len: ", 66
// | *"ThisType.Struct.decls[0].name: ", "std"
// | *"ThisType.Struct.decls[1].name: ", "c"
// | *"ThisType.Struct.decls[2].name: ", "ACTIVE_REGION" (Источник)
Мы будем использовать это, чтобы построить функции вызовов с наших функций Zig на функции C в библиотеке Лоравана.
Ранее мы захватили этот журнал вызовов: журнал вызовов для функций C в библиотеке Lorawan ...
init_event_queue
TimerInit: 0x4201c76c
SX126xIoInit: Compiled with gcc
init_gpio
...
RadioSetChannel: freq=923200000
RadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000
RadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0
RadioStandby
RadioSetModem
SX126xSetTxParams: power=13, rampTime=7
SX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1
(Источник)
Каждая строка журнала вызовов содержит имя функции Lorawan. Мы сопоставляем это с информацией от Zig Type Reflection, чтобы построить график вызовов.
Мы импортируем журнал вызовов в наше приложение Zig, как ...
/// Call Log captured for this app. From
/// https://gist.github.com/lupyuen/0871ac515b18d9d68d3aacf831fd0f5b
const call_log =
\init_event_queue
\TimerInit: 0x4201c76c
\SX126xIoInit: Compiled with gcc
\init_gpio
...
\RadioSetChannel: freq=923200000
\RadioSetTxConfig: modem=1, power=13, fdev=0, bandwidth=0, datarate=10, coderate=1, preambleLen=8, fixLen=0, crcOn=1, freqHopOn=0, hopPeriod=0, iqInverted=0, timeout=4000
\RadioSetTxConfig: SpreadingFactor=10, Bandwidth=4, CodingRate=1, LowDatarateOptimize=0, PreambleLength=8, HeaderType=0, PayloadLength=255, CrcMode=1, InvertIQ=0
\RadioStandby
\RadioSetModem
\SX126xSetTxParams: power=13, rampTime=7
\SX126xSetPaConfig: paDutyCycle=4, hpMax=7, deviceSel=0, paLut=1
\SecureElementRandomNumber: 0xbc9f21c2
\RadioSend: size=23
\00 00 00 00 00 00 00 00 00 5b b1 7b 37 e7 5e c1 4b c2 21 9c 04 48 1b
\RadioSend: PreambleLength=8, HeaderType=0, PayloadLength=23, CrcMode=1, InvertIQ=0
\TimerStop: 0x4201b86c
\TimerStart2: 0x4201b86c, 4000 ms
\callout_reset: evq=0x420131a8, ev=0x4201b86c
\
\###### =========== MLME-Request ============ ######
\###### MLME_JOIN ######
\###### ===================================== ######
\STATUS : OK
\StartTxProcess
\handle_event_queue
\DIO1 add event
\handle_event_queue: ev=0x4201b894
\RadioOnDioIrq
\RadioIrqProcess
\IRQ_TX_DONE
\TimerStop: 0x4201b86c
\TODO: RtcGetCalendarTime
\TODO: RtcBkupRead
\RadioOnDioIrq
\RadioIrqProcess
\ProcessRadioTxDone: RxWindow1Delay=4988
\RadioSleep
...
;(Источник)
Zig Compiler может обработать строку журнала вызовов, как ...
// Show the first line of the Call Log
var call_log_split = std . mem . split ( u8 , call_log , " n " );
const line = call_log_split . next ();
@compileLog ( "line:" , line );
// Shows | *"line:", []const u8{103,112,108,104,95,101,110,97,98,108,101,58,32,87,65,82,78,73,78,71,58,32,112,105,110,57,58,32,65,108,114,101,97,100,121,32,100,101,116,97,99,104,101,100}(Источник)
Итерации по всем строкам журнала вызовов мы делаем это ...
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
// TODO: Process the line
@compileLog ( "line:" , line );
...
} // End of Call Log(Источник)
Давайте сопоставляем журнал вызовов с именами функций из нашей библиотеки Lorawan ...
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
var T2 = @typeInfo ( c );
// If the C Declaration matches the Call Log...
if ( std . mem . startsWith ( u8 , line , decl . name )) {
// Dump the C Declaration
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( "Found call log" , name );
break ;
}
} // End of C Declaration
} // End of Call Log(Источник)
Приведенный выше код дает этот результат ...
| *"Found call log", "LoRaMacInitialization"
| *"Found call log", "TimerInit"
| *"Found call log", "SX126xIoInit"
| *"Found call log", "SX126xSetTx"
| *"Found call log", "SX126xSetPaConfig"
| *"Found call log", "TimerInit"
| *"Found call log", "TimerInit"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetPublicNetwork"
| *"Found call log", "RadioSleep"
| *"Found call log", "RadioSetModem"
| *"Found call log", "RadioSetPublicNetwork"
...
(Источник)
Который является списком функций Лоравана и последовательности, которую они называли.
На шаг ближе к отображению нашего структурированного графа вызовов!
Давайте расширим приведенный выше код и отобразим график наивного вызова ...
Для каждой функции Lorawan, которую мы идентифицируем в журнале вызовов ...
Мы планируем линию между функцией Lorawan и следующей функцией Lorawan в журнале вызовов
Таким образом, мы просто построим подключенную последовательность функциональных вызовов
(Да, это очень наивно!)
Мы делаем это так ...
// Draw the graph for all functions in the Call Log
var call_log_split = std . mem . split ( u8 , call_log , " n " );
var prev_name : [] const u8 = "Start" ;
@compileLog ( "flowchart TD;" ); // Top-Down Flowchart
// For every line in the Call Log...
while ( call_log_split . next ()) | line | {
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
var T2 = @typeInfo ( c );
// If the C Declaration matches the Call Log...
if ( std . mem . startsWith ( u8 , line , decl . name )) {
// Draw the graph: [previous function]-->[current function]
var name = T2 . Struct . decls [ i ]. name ;
@compileLog ( " " , prev_name , "-->" , name , ";" );
prev_name = name ;
break ;
}
} // End of C Declaration
} // End of Call Log
@compileLog ( " " , prev_name , "-->" , "End" , ";" );(Источник)
Zig Compiler дает этот результат во время компиляции ...
| *"flowchart TD;"
| *" ", "Start", *"-->", "LoRaMacInitialization", *";"
| *" ", "LoRaMacInitialization", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "SX126xIoInit", *";"
| *" ", "SX126xIoInit", *"-->", "SX126xSetTx", *";"
| *" ", "SX126xSetTx", *"-->", "SX126xSetPaConfig", *";"
| *" ", "SX126xSetPaConfig", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "TimerInit", *";"
| *" ", "TimerInit", *"-->", "RadioSetModem", *";"
...
(Источник)
Мы вручную удаляем разделители из журнала Zig Compiler, как это ...
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->RadioSetModem;
...
Чтобы получить эту блок -схему русалкой ...
Блок -схема TD;
Начало-> Лорамацинициализация;
Loramacinitialization-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> sx126xioinit;
Sx126xioinit-> sx126xsettx;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> timerinit;
Timerinit-> Timerinit;
Timerinit-> Radiosetmodem;
Radiosetmodem-> Radiosetmodem;
RadioSetModem-> RadioSetPublicNetwork;
RadiosetPublicnetwork-> Radiosleep;
Radiosle-> Radiosetmodem;
RadioSetModem-> RadioSetPublicNetwork;
RadiosetPublicNetwork-> timerinit;
Timerinit-> Timerinit;
Timerinit-> Timerinit;
Timerinit-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> Timerinit;
Timerinit-> TimerSetValue;
TimerSetValue-> Timerstop;
Timerstop-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
Radiostandby-> Radiosetchannel;
Radiosetchannel-> RadiosEtrxConfig;
RadiosEtrxConfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> RadioSetrxConfig;
RadioSetrxConfig-> radiorx;
Radiorx-> timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_header_valid;
Irq_header_valid-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_rx_done;
IRQ_RX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosleep-> Timerstop;
Timerstop-> lmhandlersend;
Lmhandlersend-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
Radiostandby-> Radiosetchannel;
Radiosetchannel-> RadiosEtrxConfig;
RadiosEtrxConfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> RadioSetrxConfig;
RadioSetrxConfig-> radiorx;
Radiorx-> timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> irq_rx_tx_timeout;
IRQ_RX_TX_TIMEOUT-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosleep-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
Radiostandby-> Radiosetchannel;
Radiosetchannel-> RadiosEtrxConfig;
RadiosEtrxConfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> RadioSetrxConfig;
RadioSetrxConfig-> radiorx;
Radiorx-> timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> irq_rx_tx_timeout;
IRQ_RX_TX_TIMEOUT-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosleep-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
Radiostandby-> Radiosetchannel;
Radiosetchannel-> RadiosEtrxConfig;
RadiosEtrxConfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> RadioSetrxConfig;
RadioSetrxConfig-> radiorx;
Radiorx-> timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> irq_rx_tx_timeout;
IRQ_RX_TX_TIMEOUT-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosleep-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Timerstart-> Timerstop;
Timerstop-> radiostandby;
Radiostandby-> Radiosetchannel;
Radiosetchannel-> RadiosEtrxConfig;
RadiosEtrxConfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> RadioSetrxConfig;
RadioSetrxConfig-> radiorx;
Radiorx-> timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> irq_rx_tx_timeout;
IRQ_RX_TX_TIMEOUT-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosleep-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> Timerstop;
Timerstop-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> Radiosetchannel;
Radiosetchannel-> Radiosettxconfig;
Radiosettxconfig-> Radiosettxconfig;
Radiosettxconfig-> radiostandby;
Radiostandby-> Radiosetmodem;
RadioSetModem-> SX126XSetTTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> Radiosend;
Radiosend-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> radioondioirq;
Radioondioirq-> radioirqprocess;
Radioirqprocess-> irq_tx_done;
IRQ_TX_DONE-> Timerstop;
Timerstop-> radioondioirq;
Radioondioirq-> radioirqprocess;
RadioirqProcess-> Radiosleep;
Radiosle-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
TimerStart-> TimerSetValue;
TimerSetValue-> TimerStart;
Timerstart-> Timerstop;
Timerstop-> Timerstart;
Что не так с супер-нановым графом вызовов, который мы произвели?
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->RadioSetModem;
...
(Источник)
Наш разговорной график не хватает структуры . Помните, что мы только построим последовательности функциональных вызовов ...
TimerInit → TimerInit не имеет смысла (потому что у нас нет рекурсивных функций)
TimerInit → RadioSetModem совершенно неправильный, потому что TimerInit -это функция низкого уровня (библиотека ловкого многопоточного чтения), тогда как RadioSetModem является функцией высокого уровня (библиотека SX1262)
Можем ли мы добавить какую -то структуру для улучшения графа вызовов?
Чтобы создать структурированный график вызовов, мы сгруппируем функции в модули, от высокого уровня до низкого уровня ...
Zig App (самый высокий уровень)
Лораван библиотека
SX1262 Библиотека
Проблемная библиотека многопоточной чтения (самый низкий уровень)
И мы убедимся, что мы никогда не проведем линию от функции низкого уровня до функции высокого уровня.
Это фрагмент того, чего мы хотим достичь ...
Блок -схема TD;
подграф Лораван;
Начинать;
Лорамацинитиализация;
конец;
Подграф SX1262;
Sx126xioinit;
Sx126xsettx;
Sx126xsetpaconfig;
конец;
подграфой ловкий;
Timerinit;
Timerinit;
конец;
Начало-> Лорамацинициализация;
Loramacinitialization-> Timerinit;
Sx126xioinit-> sx126xsettx;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> timerinit;
Давайте добавим некоторую структуру в наш график вызовов, группируя функции C в модули.
Вот как мы определяем модули, от высокого уровня до низкого уровня ...
// Define the Modules and the First / Last Functions in each Module.
// We order the Modules from High-Level to Low-Level.
var all_modules = [ _ ] Module {
// LoRaMAC Handler is the Top Level Module that drives the LoRaWAN Stack
Module {
. name = "LMHandler" ,
. first_function = "LmHandlerInit" ,
. last_function = "DisplayAppInfo" ,
. first_index = undefined ,
. last_index = undefined ,
},
// LoRaMAC Module is the implementation of the LoRaWAN Driver
Module {
. name = "LoRaMAC" ,
. first_function = "LoRaMacInitialization" ,
. last_function = "LoRaMacDeInitialization" ,
. first_index = undefined ,
. last_index = undefined ,
},
// Radio Module is the abstract interface for LoRa Radio Transceivers
Module {
. name = "Radio" ,
. first_function = "RadioInit" ,
. last_function = "RadioAddRegisterToRetentionList" ,
. first_index = undefined ,
. last_index = undefined ,
},
// SX1262 Module is the LoRa Driver for Semtech SX1262 Radio Transceiver
Module {
. name = "SX1262" ,
. first_function = "SX126xInit" ,
. last_function = "SX126xSetOperatingMode" ,
. first_index = undefined ,
. last_index = undefined ,
},
// NimBLE is the Bottom Level Module that contains Multithreading Functions like Timers and Event Queues
Module {
. name = "NimBLE" ,
. first_function = "TimerInit" ,
. last_function = "TimerGetElapsedTime" ,
. first_index = undefined ,
. last_index = undefined ,
},
};(Источник)
Обратите внимание, что мы указываем только первые и последние функции для каждого модуля.
Это потому, что функции из одного и того же модуля кластеризуются вместе в отражении типа Zig.
Затем мы отображаем каждый модуль в качестве подграфа Mermaid.js ...
/// Render all Modules and their Functions as Subgraphs
fn render_modules ( all_modules : [] Module ) void {
comptime {
// Render every Module
for ( all_modules ) | module , m | {
@compileLog ( " subgraph " , module . name , ";" );
// For every line in the Call Log...
var call_log_split = std . mem . split ( u8 , call_log , " n " );
while ( call_log_split . next ()) | line | {
var T = @typeInfo ( c );
// If the Call Log matches a C Declaration...
if ( get_decl_by_name_filtered ( all_modules , line )) | decl_index | {
// Get the Module Index for the C Declaration
if ( get_module_by_decl ( all_modules , decl_index )) | m2 | {
// If the C Declaration matches our Module Index...
if ( m == m2 ) {
// Print the Function Name
var name = T . Struct . decls [ decl_index ]. name ;
@compileLog ( " " , name , ";" );
}
} else {
// Missing Declaration
var name = T . Struct . decls [ decl_index ]. name ;
@compileLog ( "Missing Decl:" , name );
}
}
} // End of Call Log
@compileLog ( " end;" );
} // End of Module
}
}(Источник)
Каждый подграф содержит список функций, которые принадлежат модулю ...
flowchart TD;
subgraph LMHandler;
LmHandlerSend;
end;
subgraph LoRaMAC;
LoRaMacInitialization;
end;
subgraph Radio;
RadioSetModem;
RadioSetPublicNetwork;
RadioSleep;
RadioSetChannel;
RadioSetTxConfig;
RadioStandby;
RadioSend;
RadioIrqProcess;
...
end;
subgraph SX1262;
SX126xIoInit;
SX126xSetTx;
SX126xSetPaConfig;
end;
subgraph NimBLE;
TimerInit;
TimerStop;
TimerStart;
TimerSetValue;
end;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->SX126xIoInit;
...
(Источник)
Когда мы отображаем вывод с помощью Mermaid.js, мы получаем структурированный график вызовов, который выглядит более значимым ...
Блок -схема TD;
подграф Lmhandler;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
конец;
подграф Лорамак;
Лорамацинитиализация;
конец;
подграф радио;
Radiosetmodem;
Radiosetmodem;
RadiosetPublicNetwork;
Radiosle;
Radiosetmodem;
RadiosetPublicNetwork;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioirqProcess;
RadioirqProcess;
Radiosle;
Radiostandby;
Radiosetchannel;
RadiosEtrxConfig;
Radiostandby;
Radiosetmodem;
RadiosEtrxConfig;
Radiorx;
RadioirqProcess;
RadioirqProcess;
RadioirqProcess;
Radiosle;
RadioirqProcess;
Radiosetchannel;
Radiosettxconfig;
Radiosettxconfig;
Radiostandby;
RadioSetModem;
RadioSend;
RadioSend;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
RadioStandby;
RadioSetChannel;
RadioSetRxConfig;
RadioStandby;
RadioSetModem;
RadioSetRxConfig;
RadioRx;
RadioIrqProcess;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
RadioIrqProcess;
RadioSetChannel;
RadioSetTxConfig;
RadioSetTxConfig;
RadioStandby;
RadioSetModem;
RadioSend;
RadioSend;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
RadioStandby;
RadioSetChannel;
RadioSetRxConfig;
RadioStandby;
RadioSetModem;
RadioSetRxConfig;
RadioRx;
RadioIrqProcess;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
RadioIrqProcess;
RadioSetChannel;
RadioSetTxConfig;
RadioSetTxConfig;
RadioStandby;
RadioSetModem;
RadioSend;
RadioSend;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
RadioStandby;
RadioSetChannel;
RadioSetRxConfig;
RadioStandby;
RadioSetModem;
RadioSetRxConfig;
RadioRx;
RadioIrqProcess;
RadioIrqProcess;
RadioIrqProcess;
RadioSleep;
конец;
subgraph SX1262;
SX126xIoInit;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
SX126xSetTx;
SX126xSetPaConfig;
конец;
subgraph NimBLE;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerInit;
TimerStop;
TimerStart;
TimerInit;
TimerSetValue;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerSetValue;
TimerStart;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStart;
TimerStop;
TimerStop;
TimerStop;
конец;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->RadioSetModem;
RadioSetModem-->RadioSetPublicNetwork;
RadioSetPublicNetwork-->RadioSleep;
RadioSleep-->RadioSetModem;
RadioSetModem-->RadioSetPublicNetwork;
RadioSetPublicNetwork-->TimerInit;
TimerInit-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerInit;
TimerInit-->TimerSetValue;
TimerSetValue-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->LmHandlerSend;
LmHandlerSend-->RadioSetChannel;
RadioSetChannel-->RadioSetTxConfig;
RadioSetTxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->RadioSend;
RadioSend-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerSetValue;
TimerSetValue-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->TimerStop;
TimerStop-->RadioStandby;
RadioStandby-->RadioSetChannel;
RadioSetChannel-->RadioSetRxConfig;
RadioSetRxConfig-->RadioStandby;
RadioStandby-->RadioSetModem;
RadioSetModem-->RadioSetRxConfig;
RadioSetRxConfig-->RadioRx;
RadioRx-->TimerStop;
TimerStop-->TimerStart;
TimerStart-->RadioIrqProcess;
RadioIrqProcess-->TimerStop;
TimerStop-->RadioIrqProcess;
RadioIrqProcess-->RadioSleep;
RadioSleep-->TimerStop;
TimerStop-->End;
Тодо
Zig Compiler crashes when we run this code to group the C Functions by Module...
zig-bl602-nuttx/reflect.zig
Lines 825 to 1086 in f5a5c82
Here is the Output Log...
https://gist.github.com/lupyuen/5738abefa9d4c138e9d731e22d01500f
On macOS, Zig Compiler consumes over 34 GB of memory and crashes...
(On WSL, Zig Compiler hangs the WSL process when it consumes over 4 GB of memory)
This happens because our code loops repeatedly over 4,700 C Declarations while processing 1,500 lines of Raw Call Logs .
Let's optimise our code.
Our code searches for a C Declaration by looping repeatedly over 4,700 C Declarations like so...
/// Return the C Declaration Index for the Function Name.
/// We match the C Declaration Name against the start of the Function Name.
/// This is the slower, unfiltered version that searches all C Declarations.
fn get_decl_by_name ( name : [] const u8 ) ? usize {
comptime {
const T = @typeInfo ( c );
// For every C Declaration...
for ( T . Struct . decls ) | decl , i | {
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
// If the C Declaration starts with the Function Name...
if ( std . mem . startsWith ( u8 , name , decl . name )) {
// Return the C Declaration Index
return i ;
}
} // End of C Declaration
return null ; // Not found
}
}(Источник)
Which causes Zig Compiler to crash with Out Of Memory. But we don't actually need to loop through all the C Declarations!
According to our list of Modules, we call only 173 Functions . Let's fix the above function so that we loop over the 173 Functions instead...
/// Return the C Declaration Index for the Function Name.
/// We match the C Declaration Name against the start of the Function Name.
/// This is the faster, filtered version that searches C Declarations by Modules.
fn get_decl_by_name_filtered ( all_modules : [] Module , name : [] const u8 ) ? usize {
comptime {
const T = @typeInfo ( c );
// Search all Modules for the Function Name
for ( all_modules ) | module | {
// For every C Declaration in the Module...
var decl_index = module . first_index ;
while ( decl_index <= module . last_index ) {
// Get the C Declaration
var decl = T . Struct . decls [ decl_index ];
if ( std . mem . eql ( u8 , decl . name , "Radio" )) { continue ; } // Skip Radio
// If the C Declaration starts with the Function Name...
if ( std . mem . startsWith ( u8 , name , decl . name )) {
// Return the C Declaration Index
return decl_index ;
}
decl_index += 1 ;
} // End of C Declaration
} // End of Module
return null ; // Not found
}
}(Источник)
And our code runs OK with Zig Compiler!
(macOS, not WSL though)
Building a Call Graph at Compile-Time with Zig Compiler looks pretty challenging.
I hope that future versions of the Zig Compiler will support the following...
Type Reflection for C Declarations will provide the full path of source files
(Makes it easier to identify the module for each declaration)
@compileLog will emit any kind of strings
(So that @compileLog can generate proper Mermaid.js strings like " Start-->LoRaMacInitialization; ")