Lisez les articles ...
"Zig sur RISC-V BL602: PEEEK RAPIDE AVEC APACH NUTTX RTOS"
"Créez une application IoT avec Zig et Lorawan"
Pour construire l'application Hello Zig pour Nuttx sur 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 succeedDémarrer Nuttx et entrez-le sur la coque Nuttx ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
Pour l'application Lorawan Zig, voyez ceci ...
Pour compiler la bibliothèque LORA SX1262 en C avec un compilateur en zig, voir ceci ...
Voici comment nous avons fait fonctionner Zig et Lorawan sur BL602 NUTTX ...
Apache Nuttx RTOS est regroupé d'une application en zig simple ... Exécutons ceci sur 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 ;
}Nous avons corrigé les 2 dernières lignes pour rendre le compilateur en zig heureux ...
// 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 ;La version originale est ici: hello_zig_main.zig
Pour activer l'application zig dans Nuttx ...
make menuconfigSélectionnez "Configuration de l'application> Exemples> Hello Zig Exemple"
Enregistrez la configuration et quittez Menuconfig.
Quand nous construisons Nuttx ...
makeNous voyons cette erreur ...
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'
(Source)
Ce qui ressemble à ce problème ...
apache / nuttx # 6219
Cela semble être causé par la construction NUTTX sans appeler le compilateur en zig.
Mais pas de soucis! Compilons nous-mêmes l'application en zig et lions vers Nuttx.
Voici comment nous compilons notre application en zig pour RISC-V BL602 et la lions avec 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 Pourquoi la cible riscv32-freestanding-none ?
Les cibles en zig ont le formulaire <arch><sub>-<os>-<abi> ...
riscv32 : Parce que BL602 est un processeur RISC-V 32 bits
freestanding : parce que les cibles intégrées n'ont pas besoin d'un système d'exploitation
none : parce que les cibles intégrées ne spécifient pas l'ABI
Pourquoi le CPU cible est- sifive_e76 ?
BL602 est désigné comme RV32IMACF ...
| Désignation | Signification |
|---|---|
RV32I | RISC-V 32 bits avec des instructions entières de base |
M | Multiplication entière + division |
A | Instructions atomiques |
C | Instructions compressées |
F | Point flottant à une seule précision |
(Source)
Parmi toutes les cibles en zig, seul sifive_e76 a la même désignation ...
$ zig targets
...
" sifive_e76 " : [ " a " , " c " , " f " , " m " ],(Source)
Ainsi, nous utilisons sifive_e76 comme objectif CPU.
Alternativement, nous pouvons utiliser baseline_rv32-d comme objectif CPU ...
# # 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.zigParce que...
baseline_rv32 signifie RV32IMACFD
(D pour le point flottant à double précision)
-d signifie supprimer le point flottant à double précision (D)
(Mais gardez le point flottant à une seule précision)
(En savoir plus sur les drapeaux de fonction RISC-V. Merci Matheus!)
Lors de la liaison de l'application en zig compilée avec NUTTX, nous voyons cette erreur ...
$ 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 modulesEn effet
# # 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"(Source)
(Nuttx a été compilé avec les drapeaux GCC -march=rv32imafc -mabi=ilp32f )
Tandis que le compilateur zig produit un fichier d'objet avec un logiciel à point flottant 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"(Source)
GCC ne nous permettra pas de lier des fichiers d'objets avec des ABI à point flottante et à point flottant du logiciel!
(Pourquoi le compilateur en zig a-t-il produit un fichier d'objet avec ABI à point flottant logiciel, quand sifive_e76 prend en charge
Le compilateur zig génère un fichier d'objet avec un logiciel à point flottant ABI (interface binaire d'application) ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABICela ne sera pas lié à Nuttx car NUTTX est compilé avec ABI à virgule flottante matérielle.
Nous corrigeons ceci en modifiant l'en-tête Elfe ...
Edit hello_zig_main.o dans un éditeur hexadécimal
(Comme VScode Hex Editor)
Changer l'octet 0x24 (drapeaux) de 0x01 (flotteur doux) à 0x03 (flotteur dur)
(Voir ceci)
Nous vérifions que le fichier d'objet a été changé en matériel à point flottante ABI ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x3, RVC, single-float ABIIl s'agit désormais d'ABI du matériel à virgule flottante et de liaison avec NUTTX.
Maintenant, nous lions le fichier d'objet modifié avec 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
makeLa construction Nuttx devrait maintenant réussir.
Est-il vraiment acceptable de changer l'Abi comme ça?
Eh bien, techniquement, l' ABI est correctement généré par le compilateur en 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 "La dernière ligne se traduit par RV32IMACF , ce qui signifie que l'ensemble d'instructions RISC-V est en effet ciblé pour le point flottant matériel .
Nous modifions uniquement l' en-tête ELF , car il ne semblait pas refléter le bon ABI pour le fichier objet.
Y a-t-il une correction appropriée pour cela?
À l'avenir, le compilateur en zig pourrait nous permettre de spécifier l' ABI à virgule flottante comme cible ...
# # Compile the Zig App for BL602
# # ("ilp32f" means Hardware Floating-Point ABI)
zig build-obj
-target riscv32-freestanding-ilp32f
...(Voir ceci)
Restez à l'écoute!
Pouvons-nous corriger l'en-tête Elfe via la ligne de commande?
Oui, nous pouvons corriger l'en-tête elfe via la ligne de commande ...
xxd -c 1 hello_zig_main.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - hello_zig_main2.oLa construction Nuttx réussit. Zig fonctionne bien sur Nuttx BL602!
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
N'oubliez pas que nous avons écrasé hello.o avec notre fichier d'objet compilé en zig.
La construction NUTTX échouera à moins que nous ne fournissions la fonction 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'
C'est pourquoi nous définissons hello_main dans notre application en zig ...
pub export fn hello_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}(Source)
Ce qui signifie que l'application hello appellera également notre code en zig ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello
Hello, Zig!
Pine64 Pinecone Bl602 Board (à droite) connecté à Semtech SX1262 LORA Transmetteur (à gauche) sur SPI
Le compilateur Zig fonctionnera-t-il en remplacement de dépôt pour la compilation de bibliothèques Nuttx?
Testons-le sur la bibliothèque LORA SX1262 pour Apache Nuttx RTOS!
Voici comment Nuttx compile la bibliothèque LORA SX1262 avec 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.oNous apportons ces changements ...
Changer riscv64-unknown-elf-gcc en zig cc
Ajouter la cible -target riscv32-freestanding-none -mcpu=baseline_rv32-d
Supprimer -march=rv32imafc
Et nous exécutons ceci ...
# # 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
makeLe compilateur en zig montre ces erreurs ...
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];
^
Nous corrigeons cela en incluant les fichiers d'en-tête droits ...
#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__)Dans ces fichiers source ...
(Voir les changements)
Nous insérons ce code pour nous dire (au moment de l'exécution) s'il a été compilé avec le compilateur en zig ou 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__(Source)
Compilé avec zig cc , la bibliothèque Lora SX1262 s'exécute OK sur 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
(Voir le journal complet)
Compilons l'énorme bibliothèque Lorawan avec le compilateur en zig.
Nuttx compile la bibliothèque de Lorawan comme celle-ci ...
# # 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.oNous passons au compilateur en 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
makeNous incluons les fichiers d'en-tête droits dans 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__)(Voir les changements)
Loramac.c compile ok avec le compilateur en zig.
TODO: compiler les autres fichiers de la bibliothèque de Lorawan avec build.zig
https://ziglang.org/documentation/master/#zig-build-ystem
TODO: tester la bibliothèque de Lorawan
Maintenant, nous compilons l'application Lorawan lorawan_test_main.c avec le compilateur zig.
Nuttx compile l'application lorawan lorawan_test_main.c comme ceci ...
# # 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.oNous passons au compilateur en zig ...
# # 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
makeNous incluons les fichiers d'en-tête droits dans 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__)(Voir les changements)
Compilé avec zig cc , l'application Lorawan s'exécute OK sur 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
(Voir le journal complet)
Le compilateur en zig peut auto-transposer le code C en zig. (Voir ceci)
Voici comment nous transférons automatiquement notre application Lorawan Lorawan_test_Main.c de C à Zig ...
Changer zig cc en zig translate-c
Entourer les drapeaux C par -cflags ... --
Comme ça...
# # 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.zigVoici le code C d'origine: lorawan_test_main.c
Et l'auto-traduction de C à zig: traduit / lorawan_test_main.zig
Voici un extrait du code C d'origine ...
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 ;
}(Source)
Et le code en zig traditionné automatique ...
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 ;
}(Source)
Nous nous référerons à ce code en zig translaté automatiquement lorsque nous convertirons manuellement notre application Lorawan lorawan_test_main.c de C à zig dans la section suivante ...
Pine64 Pinedio Stack Bl604 (à gauche) Talking Lorawan à Rakwireless Wisgate (à droite)
Enfin, nous convertissons l'application lorawan lorawan_test_main.c de C en zig, pour montrer que nous pouvons créer des applications IoT complexes en zig.
L'application Lorawan s'exécute sur Pinedio Stack BL604 (RISC-V). L'application se connecte à une passerelle Lorawan (comme ChirpStack ou le réseau Things) et envoie un paquet de données à intervalles réguliers.
Voici le code C d'origine: lorawan_test_main.c
(700 lignes de code C)
Et notre application Lorawan Zig convertie: Lorawan_test.zig
(673 lignes de code zig)
/// 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 PacketPour compiler l'application 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
makeNotre application Lorawan Zig Lorawan_test.zig se compile avec le compilateur en zig après avoir fait les correctifs suivants ...
Certaines parties de l'application Lorawan Zig Lorawan_test.zig peuvent devenir difficiles à convertir de C en zig, comme ce code 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
);(Source)
Qui a des types signés ( randr ) et non signés ( APP_TX_DUTYCYCLE ).
Nous obtenons de l'aide en faisant référence au code en zig traditionné automatique: traduit / 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
)
);Qui résout les types conflictuels en jetant le résultat signé pour devenir non signé.
Lorsque nous faisons référence LmHandlerCallbacks dans notre application Lorawan Zig Lorawan_test.zig ...
_ = & LmHandlerCallbacks ;Le compilateur Zig montrera cette erreur de type opaque ...
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,
^
L'erreur de type opaque est expliquée ici ...
"Étendre un projet C / C ++ avec un zig"
"Échecs de traduction"
Tracez notre erreur de type opaque ...
export fn OnMacMlmeRequest (
status : c.LoRaMacStatus_t ,
mlmeReq : [ * c ] c.MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void {
c . DisplayMacMlmeRequestUpdate ( status , mlmeReq , nextTxIn );
} Notre fonction OnMacMlmeRequest a un paramètre de type MlmeReq_t , d'imposé automatiquement par le compilateur en zig comme ...
pub const MlmeReq_t = struct_sMlmeReq ;
pub const struct_sMlmeReq = extern struct {
Type : Mlme_t ,
Req : union_uMlmeParam ,
ReqReturn : RequestReturnParam_t ,
}; Qui contient un autre type de type auto- union_uMlmeParam ...
pub const union_uMlmeParam = extern union {
Join : MlmeReqJoin_t ,
TxCw : MlmeReqTxCw_t ,
PingSlotInfo : MlmeReqPingSlotInfo_t ,
DeriveMcKEKey : MlmeReqDeriveMcKEKey_t ,
DeriveMcSessionKeyPair : MlmeReqDeriveMcSessionKeyPair_t ,
}; Qui contient un MlmeReqPingSlotInfo_t ...
pub const MlmeReqPingSlotInfo_t = struct_sMlmeReqPingSlotInfo ;
pub const struct_sMlmeReqPingSlotInfo = extern struct {
PingSlot : PingSlotInfo_t ,
}; Qui contient un PingSlotInfo_t ...
pub const PingSlotInfo_t = union_uPingSlotInfo ;
pub const union_uPingSlotInfo = extern union {
Value : u8 ,
Fields : struct_sInfoFields ,
}; Qui contient un struct_sInfoFields ...
pub const struct_sInfoFields = opaque {}; Mais les champs de struct_sInfoFields ne sont pas connus du compilateur en zig!
Si nous nous référons au code C d'origine ...
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 ;(Source)
Nous voyons que sInfoFields contient des champs de bits, que le compilateur en zig est incapable de traduire.
Plus tôt, nous avons vu que cela ne parvient pas à compiler dans notre application Lorawan Zig Lorawan_test.zig ...
_ = & LmHandlerCallbacks ; En effet, LmHandlerCallbacks fait référence au type de type automatique MlmeReq_t , qui contient des champs bits et ne peut pas être traduit par le compilateur en zig.
Converons MlmeReq_t en un type opaque, car nous n'accéderons pas aux champs de toute façon ...
/// 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 {};(Source)
Nous convertissons LmHandlerCallbacks pour utiliser notre type opaque 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 ,
};(Source)
Nous modifions toutes les références MlmeReq_t à implémente automatiquement de ...
[ * c ] c . MlmeReq_tÀ notre type opaque ...
* MlmeReq_t Nous modifions également toutes les références LmHandlerCallbacks_t à importer automatiquement de ...
[ * c ] c . LmHandlerCallbacks_t À notre LmHandlerCallbacks_t converti ...
* LmHandlerCallbacks_tCe qui signifie que nous devons importer nous-mêmes les fonctions Lorawan affectées ...
/// 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 ;(Source)
Après avoir réparé le type opaque, le compilateur Zig compile avec succès notre application de test Lorawan lorawan_test.zig yay!
Lors de la compilation de notre application de test de Lorawan lorawan_test.zig, nous voyons cette erreur macro ...
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
Selon les documents zig, cela signifie que le compilateur en zig n'a pas traduit une macro C ...
Nous définissons donc LL -mêmes ...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" ); ( LL est le suffixe "long" pour les constantes C, qui n'est probablement pas nécessaire lorsque nous importons les types C et les fonctions en zig)
Ensuite, le compilateur Zig émet cette erreur ...
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
Qui fait référence à cette ligne dans stdint.h ...
#define __int_c_join ( a , b ) a ## b La macro __int_c_join échoue car le suffixe LL est maintenant vide et l'opérateur de concaténation ## échoue.
Nous redéfinissons la macro __int_c_join sans l'opérateur de concaténation ## ...
/// 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.hMaintenant, le compilateur zig compile avec succès notre application de test de Lorawan lorawan_test.zig
Le compilateur en zig s'écrase lorsqu'il essaie d'initialiser la structure de la minuterie au démarrage ...
/// 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(Source)
Nous initialisons donc la structure de la minuterie dans la fonction principale à la place ...
/// 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 );(Source)
Après avoir résolu les problèmes ci-dessus, nous testons l'application Lorawan Zig sur 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
(Voir le journal complet)
Lorawan Zig App Lorawan_test.zig rejoint avec succès le réseau Lorawan (Chirpstack sur Rakwireless Wisgate) et envoie un paquet de données au Lorawan Gateway Yay!
Le compilateur en zig révèle des idées intéressantes lors de l'auto-transport notre code C en zig.
Ce code C copie un tableau, octet par octet ...
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
}(Source)
Voici le code en zig traditionné automatique ...
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 ;
}(Source)
Notez que l'indexation du tableau en C ...
// Array Indexing in C...
UnfragmentedData [ addr + i ]Se traduit à cela en zig ...
// Array Indexing in Zig...
UnfragmentedData [ addr +% i ] + En C devient +% en zig!
Qu'est-ce que +% en zig?
C'est l'opérateur en zig pour l'ajout de wraparound .
Ce qui signifie que le résultat revient à 0 (et au-delà) si l'ajout déborde l'entier.
Mais ce n'est pas ce que nous voulions, car nous ne nous attendons pas à l'addition de débordement. C'est pourquoi dans notre code en zig final converti, nous retournons +% de retour à + ...
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
}(Source)
Que se passe-t-il si l'ajout déborde?
Nous verrons une erreur d'exécution ...
panic: integer overflow
(Source)
Ce qui est probablement une bonne chose, pour nous assurer que nos valeurs sont sensibles.
Et si notre index de tableau sort des limites?
Nous obtiendrons cette erreur d'exécution ...
panic: index out of bounds
(Source)
Voici la liste des contrôles de sécurité effectués par Zig lors de l'exécution ...
Si nous préférons vivre imprudemment, c'est ainsi que nous désactivons les contrôles de sécurité ...
Certaines fonctionnalités de débogage ne semblent pas fonctionner? Comme unreachable , std.debug.assert et std.debug.panic ?
C'est parce que pour les plates-formes intégrées, nous devons implémenter notre propre gestionnaire de panique ...
"Utilisation de zig pour fournir des traces de pile sur la panique du noyau pour un système d'exploitation nu sur les os"
Handler de panique par défaut: std.debug.default_panic
Avec notre propre gestionnaire de panique, cet échec d'affirmation ...
// Create a short alias named `assert`
const assert = std . debug . assert ;
// Assertion Failure
assert ( TxPeriodicity != 0 );Montrera cette trace de pile ...
!ZIG PANIC!
reached unreachable code
Stack Trace:
0x23016394
0x23016ce0
Selon notre démontage RISC-V, la première adresse 23016394 n'est pas intéressante, car elle est à l'intérieur de la fonction 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>
Mais la deuxième adresse 23016ce0 révèle l'affirmation qui a échoué ...
/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
Ceci est notre implémentation du gestionnaire de panique en zig ...
/// 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 ) {}
}(Source)
Nous avons mis en œuvre de la journalisation de débogage std.log.debug qui est décrite ici ...
Voici comment nous appelons std.log.debug pour imprimer un message de journal ...
// 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 .{ ... } Crée une structure anonyme avec un nombre variable d'arguments qui seront transmis à std.log.debug pour l'impression.
Vous trouverez ci-dessous notre implémentation de 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 );
}(Source)
Cette implémentation appelle puts() , qui est prise en charge par Apache Nuttx RTOS car elle est conforme à POSIX .
Le code C d'origine et le code en zig converti pour notre application Lorawan sont très similaires.
Voici la fonction principale de notre code C d'origine ...
/// 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 ;
}(Source)
Et la fonction principale de notre code en zig converti (après un certain frottement) ...
/// 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 ;
}(Source)
TODO: Nettoyez les noms de types, fonctions et variables
TODO: Lisez le capteur de température interne
TODO: coder les données du capteur de température avec Tinycbor et transmettre au réseau de choses
https://lupyuen.github.io/articles/cbor2
TODO: Surveillez les données des capteurs avec Prometheus et Grafana
https://lupyuen.github.io/articles/prometheus
TODO: Ajouter un nouveau code avec @import()
https://zig.news/mattnite/import-and-packages-23mb
TODO: Avons-nous besoin d'aligner des tampons sur 32 bits lors de l'exportation vers 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 );Réflexion de type zig ... pouvons-nous l'utiliser pour générer un graphique d'appel structuré pour les bibliothèques C ... comme pour la bibliothèque Lorawan? ?
Ce programme en zig importe la bibliothèque de Lorawan à partir de C et jette les types et fonctions de 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
}(Source)
( @typeInfo est expliqué ici)
Lorsque le compilateur zig compile le code ci-dessus, nous le voyons à Compile-Time ...
$ 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);
^
(Source)
Qui est une liste des types C et des fonctions de la bibliothèque Lorawan.
Utilisons ceci pour visualiser le graphique d'appel (par module) pour la bibliothèque Lorawan.
Nous rendrons le graphique d'appel avec sirmaid.js ...
https://mermaid-js.github.io/mermaid/#/./flowchart?id=flowCharts
Et nous regrouperons les fonctions Lorawan dans le graphique d'appel par le module Lorawan (Subgraph), afin que nous puissions voir les appels à travers les modules Lorawan.
Dans le code ci-dessus, pourquoi avons-nous utilisé T2.Struct.decls[i].name au lieu de decl.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 );
}
}Nous nous attendons à ce que ce code imprime le nom de la déclaration ...
@compileLog ( "decl.name: " , decl . name );Comme c'est ...
"decl.name: ", "LmnStatus_t"
Mais étrangement, il imprime les octets ...
"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
Le compilateur en zig semble interpréter le nom différemment après avoir référencé le nom plus tôt ...
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) { ...Nous utilisons donc cette solution de contournement à la place ...
// Get a fresh reference to the Type Info
var T2 = @typeInfo ( c );
// This works OK
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );Ce qui produit le résultat dont nous avons besoin ...
"decl.name: ", "LmnStatus_t"
Pouvons-nous identifier automatiquement le module Lorawan pour chaque fonction Lorawan, en analysant les macros d'en-tête d' HEADER_NAME_H__ C?
Essayons ceci pour éliminer toutes les déclarations et les macros importées de 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(Source)
Nous obtenons cette liste des fonctions de Lorawan et des macros de Lorawan ...
| *"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__"
(Source)
Ce qui n'est pas utile. LmHandlerInit est en fait déclaré dans le fichier d'en-tête C pour __LORAMAC_HANDLER_H__ . Mais d'une manière ou d'une autre, la réflexion de type zig déplace LmHandlerInit jusqu'au sommet, avant que __LORAMAC_HANDLER_H__ n'apparaisse.
Il semble donc que nous devons regrouper manuellement les fonctions de Lorawan en modules Lorawan.
Les fonctions de Lorawan semblent être séquencées en fonction du fichier d'en-tête C, nous n'avons donc qu'à marquer manuellement la première fonction Lorawan pour chaque module Lorawan. Comme ça...
LmHandlerInit → __LORAMAC_HANDLER_H__
LoRaMacInitialization → __LORAMAC_H__
RegionCommonValueInRange → __REGIONCOMMON_H__
SX126xInit → __SX126x_H__
SX126xIoInit → __SX126x_BOARD_H__
Notre application en zig peut faire la réflexion sur elle-même pour découvrir ses types, constantes, variables et fonctions ...
// 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" (Source)
Nous allons l'utiliser pour tracer les appels de fonction de nos fonctions en zig aux fonctions C de la bibliothèque Lorawan.
Plus tôt, nous avons capturé ce journal d'appels: un journal des appels aux fonctions C de la bibliothèque 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
(Source)
Chaque ligne du journal d'appel contient le nom de la fonction Lorawan. Nous allons correspondre à cela avec les informations de la réflexion sur type zig pour tracer le graphique d'appel.
Nous importons le journal des appels dans notre application en zig comme ça ...
/// 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
...
;(Source)
Le compilateur en zig peut traiter le journal d'appel en ligne par ligne comme ça ...
// 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}(Source)
Pour parcourir toutes les lignes du journal des appels, nous faisons cela ...
// 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(Source)
Faisons correspondre le journal des appels avec les noms de fonction de notre bibliothèque 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(Source)
Le code ci-dessus produit ce résultat ...
| *"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"
...
(Source)
Qui est une liste des fonctions de Lorawan et la séquence qu'ils ont été appelée.
Un pas de plus pour rendre notre graphique d'appel structuré!
Étendons le code ci-dessus et rendons un graphique d'appel naïf ...
Pour chaque fonction Lorawan que nous identifions dans le journal des appels ...
Nous tracons une ligne entre la fonction Lorawan et la fonction Lorawan suivante dans le journal d'appel
Nous traçons donc simplement la séquence connectée des appels de fonction
(Oui c'est super naïf!)
Nous le faisons comme ça ...
// 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" , ";" );(Source)
Le compilateur en zig produit ce résultat au temps de compilation ...
| *"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", *";"
...
(Source)
Nous supprimons manuellement les délimiteurs du journal du compilateur en zig ci-dessus comme celui-ci ...
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;
...
Pour obtenir cette sirène.js Organing ...
Organigramme TD;
Démarrer -> loramacinialisation;
Loramacinialisation -> temporerinit;
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;
Radiosleep -> 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;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERINIT;
TIMERINIT -> TIMERSETVALUE;
TimersetValue -> timerstop;
TIMERSOP -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> RADIOSTANDBY;
Radiostandby -> Radiosetchannel;
Radiosetchannel -> RadiosetTrXConfig;
RadiosetrxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiosetModem -> radiosetrXConfig;
RadioSetxConfig -> radiorx;
RADIORX -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_header_valid;
Irq_header_valid -> radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_rx_done;
Irq_rx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timerstop;
TIMERSOP -> LMHandlersend;
LMHandlersend -> RadioSetchannel;
Radiosetchannel -> RadiOSetTxConfig;
RadiOSetTxConfig -> RadiOSetTxConfig;
RadiosettxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiOSetModem -> SX126XSETTTX;
Sx126xsetTx -> sx126xsetpaconfig;
SX126XSETPACONFIG -> RADIOSEND;
Radiosend -> radiosend;
Radiosend -> timerstop;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> RADIOSTANDBY;
Radiostandby -> Radiosetchannel;
Radiosetchannel -> RadiosetTrXConfig;
RadiosetrxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiosetModem -> radiosetrXConfig;
RadioSetxConfig -> radiorx;
RADIORX -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_rx_tx_timeout;
Irq_rx_tx_timeout -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timerstop;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> 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;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
RadioOnitier -> Radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> RADIOSTANDBY;
Radiostandby -> Radiosetchannel;
Radiosetchannel -> RadiosetTrXConfig;
RadiosetrxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiosetModem -> radiosetrXConfig;
RadioSetxConfig -> radiorx;
RADIORX -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_rx_tx_timeout;
Irq_rx_tx_timeout -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timerstop;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> 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;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> RADIOSTANDBY;
Radiostandby -> Radiosetchannel;
Radiosetchannel -> RadiosetTrXConfig;
RadiosetrxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiosetModem -> radiosetrXConfig;
RadioSetxConfig -> radiorx;
RADIORX -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_rx_tx_timeout;
Irq_rx_tx_timeout -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timerstop;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> 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;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> RADIOSTANDBY;
Radiostandby -> Radiosetchannel;
Radiosetchannel -> RadiosetTrXConfig;
RadiosetrxConfig -> radiostandby;
Radiostandby -> radiosetmodem;
RadiosetModem -> radiosetrXConfig;
RadioSetxConfig -> radiorx;
RADIORX -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> RadioOnitierq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_rx_tx_timeout;
Irq_rx_tx_timeout -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timerstop;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSTOP;
TIMERSOP -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> 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;
TIMERSOP -> TIMERSTART;
TIMERSTART -> Radioondioirq;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> irq_tx_done;
Irq_tx_done -> timerstop;
TIMERSOP -> RadioOnidioRQ;
Radioondioirq -> RadioIrqprocess;
RadioIrqprocess -> radiosleep;
Radiosleep -> timersetValue;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
TIMERSTART -> TIMERSETVALUE;
TimersetValue -> timerstart;
TIMERSTART -> TIMERSTOP;
TIMERSOP -> TIMERSTART;
Quel est le problème avec le graphique d'appel super-naïf que nous avons produit?
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->RadioSetModem;
...
(Source)
Notre graphique d'appel manque de structure . N'oubliez pas que nous ne complotons que la séquence des appels de fonction ...
TimerInit → TimerInit n'a pas de sens (car nous n'avons pas de fonctions récursives)
TimerInit → RadioSetModem est totalement faux car TimerInit est une fonction de bas niveau (bibliothèque multithreading Nimble), tandis que RadioSetModem est une fonction de haut niveau (bibliothèque SX1262)
Pouvons-nous ajouter une structure pour améliorer le graphique d'appel?
Pour produire un graphique d'appel structuré, nous regrouperons les fonctions en modules, de haut niveau à bas niveau ...
Application en zig (niveau le plus élevé)
Bibliothèque de Lorawan
Bibliothèque SX1262
Bibliothèque multithreading agile (niveau le plus bas)
Et nous nous assurerons de ne jamais tracer une ligne d'une fonction de bas niveau à une fonction de haut niveau.
Ceci est un extrait de ce que nous voulons réaliser ...
Organigramme TD;
Sub-Graph Lorawan;
Commencer;
Loramacinialisation;
fin;
Sub-Graph SX1262;
SX126XIOINIT;
Sx126xsetTx;
Sx126xsetpaconfig;
fin;
subgraph agile;
Timerinit;
Timerinit;
fin;
Démarrer -> loramacinialisation;
Loramacinialisation -> temporerinit;
SX126XIOINIT -> SX126XSETTX;
Sx126xsetTx -> sx126xsetpaconfig;
SX126XSETPACONFIG -> TIMERINIT;
Ajoutons une structure à notre graphique d'appel en regroupant les fonctions C en modules.
C'est ainsi que nous définissons les modules, du niveau élevé à un niveau bas ...
// 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 ,
},
};(Source)
Notez que nous spécifions uniquement les première et dernières fonctions pour chaque module.
En effet, les fonctions du même module sont regroupées dans la réflexion de type zig.
Ensuite, nous rendons chaque module en tant que sous-graphe sirène.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
}
}(Source)
Chaque sous-graphique contient une liste de fonctions appartenant au 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;
...
(Source)
Lorsque nous rendons la sortie avec sirmaid.js, nous obtenons un graphique d'appel structuré qui a l'air plus significatif ...
Organigramme TD;
Sub-Graph LMHandler;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
LmHandlersend;
fin;
Sub-Graph Loramac;
Loramacinialisation;
fin;
Radio Sub-Graph;
Radiosetmodem;
Radiosetmodem;
RadiosetPublicNetwork;
Radiosleep;
Radiosetmodem;
RadiosetPublicNetwork;
RadioSetchannel;
RadiosetTxConfig;
RadiosetTxConfig;
Radiostandby;
Radiosetmodem;
Radiosend;
Radiosend;
RadioIrqprocess;
RadioIrqprocess;
Radiosleep;
Radiostandby;
RadioSetchannel;
RadiosetrXConfig;
Radiostandby;
Radiosetmodem;
RadiosetrXConfig;
Radiorx;
RadioIrqprocess;
RadioIrqprocess;
RadioIrqprocess;
RadioIrqprocess;
RadioIrqprocess;
RadioIrqprocess;
RadioIrqprocess;
Radiosleep;
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;
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;
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;
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;
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;
fin;
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;
fin;
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;
fin;
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;
FAIRE
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
}
}(Source)
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
}
}(Source)
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; ")