Lea los artículos ...
"Zig en RISC-V BL602: Peek rápido con Apache Nuttx RTO"
"Construya una aplicación IoT con Zig y Lorawan"
Para construir la aplicación Hello Zig para NUTTX en 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 succeedArranque nuttx e ingrese esto en la carcasa Nuttx ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
Para la aplicación Lorawan Zig, vea esto ...
Para compilar la biblioteca Lora SX1262 en C con el compilador Zig, vea esto ...
Así es como hicimos Zig y Lorawan Run en BL602 Nuttx ...
Apache Nuttx RTOS se incluye con una aplicación Zig simple ... Ejecutemos esto en 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 ;
}Arreglamos las últimas 2 líneas para hacer feliz al compilador de zig ...
// 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 versión original está aquí: hello_zig_main.zig
Para habilitar la aplicación Zig en Nuttx ...
make menuconfigSeleccione "Configuración de la aplicación> Ejemplos> Hello Zig Ejemplo"
Guarde la configuración y salga de Menuconfig.
Cuando construimos Nuttx ...
makeVemos este error ...
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'
(Fuente)
Que se parece a este problema ...
Apache/Nuttx#6219
Esto parece ser causado por la construcción de Nuttx que no llama al compilador ZIG.
¡Pero no se preocupe! Compilemos la aplicación Zig nosotros mismos y vinculemos a Nuttx.
Así es como compilamos nuestra aplicación Zig para RISC-V BL602 y la vinculamos con 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 ¿Por qué el objetivo riscv32-freestanding-none ?
Los objetivos zig tienen la forma <arch><sub>-<os>-<abi> ...
riscv32 : porque BL602 es un procesador RISC-V de 32 bits
freestanding : porque los objetivos integrados no necesitan un sistema operativo
none : Porque los objetivos integrados no especifican el ABI
¿Por qué es la CPU de destino sifive_e76 ?
BL602 se designa como RV32ImAcf ...
| Designación | Significado |
|---|---|
RV32I | RISC-V de 32 bits con instrucciones enteras base |
M | Multiplicación entera + división |
A | Instrucciones atómicas |
C | Instrucciones comprimidas |
F | Punto flotante de una sola precisión |
(Fuente)
Entre todos los objetivos en zig, solo sifive_e76 tiene la misma designación ...
$ zig targets
...
" sifive_e76 " : [ " a " , " c " , " f " , " m " ],(Fuente)
Por lo tanto, usamos sifive_e76 como nuestro objetivo de CPU.
Alternativamente, podemos usar baseline_rv32-d como nuestro objetivo de 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.zigPorque...
baseline_rv32 significa RV32ImAcfd
(D para punto flotante de doble precisión)
-d significa eliminar el punto flotante de doble precisión (D)
(Pero mantenga el punto flotante de una precisión única)
(Más sobre banderas de características Risc-V. ¡Gracias Matheus!)
Al vincular la aplicación ZIG compilada con NutTX, vemos este error ...
$ 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 modulesEsto se debe a que NutTX fue compilado para el ABI de punto flotante de hardware (de precisión única) (interfaz binaria de aplicación) ...
# # 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"(Fuente)
(NutTX se compiló con las banderas GCC -march=rv32imafc -mabi=ilp32f )
Mientras que el compilador ZIG produce un archivo de objetos con ABI de punto flotante de software ...
$ 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"(Fuente)
¡GCC no nos permitirá vincular archivos de objetos con un punto flotante de software y un abis de punto flotante de hardware!
(¿Por qué el compilador ZIG produjo un archivo de objeto con ABI de punto flotante de software, cuando sifive_e76 admite un punto flotante de hardware? Vea esto)
El compilador ZIG genera un archivo de objeto con ABI de punto flotante de software (interfaz binaria de aplicación) ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x1, RVC, soft-float ABIEsto no se vinculará con NutTX porque NUTTX se compila con ABI de punto flotante de hardware.
Solucionamos esto modificando el encabezado Elf ...
Editar hello_zig_main.o en un editor hexadecimal
(Como el editor Hex VSCode)
Cambiar byte 0x24 (banderas) de 0x01 (flotador suave) a 0x03 (flotador duro)
(Ver esto)
Verificamos que el archivo de objeto se haya cambiado a Hardware Floating Point ABI ...
# # Dump the ABI for the compiled app
$ riscv64-unknown-elf-readelf -h -A hello_zig_main.o
...
Flags: 0x3, RVC, single-float ABIEste es ahora ABI de punto flotante de hardware y se vinculará con NutTX.
Ahora vinculamos el archivo de objeto modificado con 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 construcción de Nuttx ahora debería tener éxito.
¿Está realmente bien cambiar el abi así?
Bueno, técnicamente, el ABI es generado correctamente por el compilador 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 última línea se traduce a RV32IMACF , lo que significa que el conjunto de instrucciones RISC-V está dirigido al punto de flotación de hardware .
Solo estamos editando el encabezado ELF , porque no parecía reflejar el ABI correcto para el archivo de objeto.
¿Hay una solución adecuada para esto?
En el futuro, el compilador ZIG podría permitirnos especificar el punto flotante ABI como el objetivo ...
# # Compile the Zig App for BL602
# # ("ilp32f" means Hardware Floating-Point ABI)
zig build-obj
-target riscv32-freestanding-ilp32f
...(Ver esto)
¡Manténganse al tanto!
¿Podemos parchear el encabezado ELF a través de la línea de comandos?
Sí, podemos parchear el encabezado ELF a través de la línea de comandos ...
xxd -c 1 hello_zig_main.o
| sed ' s/00000024: 01/00000024: 03/ '
| xxd -r -c 1 - hello_zig_main2.oLa construcción de Nuttx tiene éxito. ¡Zig funciona bien en Nuttx BL602!
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello_zig
Hello, Zig!
Recuerde que sobrescribimos hello.o con nuestro archivo de objeto compilado en zig.
La compilación de Nuttx fallará a menos que proporcionemos la función 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'
Por eso definimos hello_main en nuestra aplicación Zig ...
pub export fn hello_main (
_argc : c_int ,
_argv : [ * ] const [ * ] const u8
) c_int {
_ = _argc ;
_ = _argv ;
_ = printf ( "Hello, Zig! n " );
return 0 ;
}(Fuente)
Lo que significa que la aplicación hello también llamará a nuestro código Zig ...
NuttShell (NSH) NuttX-10.3.0-RC2
nsh> hello
Hello, Zig!
Pine64 Pinecone BL602 Board (derecha) conectada a Semtech SX1262 Lora Transceptor (izquierda) sobre SPI
¿Funcionará el compilador ZIG como reemplazo de GCC para compilar bibliotecas NutTX?
¡Probáramos en la biblioteca Lora SX1262 para Apache Nuttx RTO!
Así es como NutTX compila la biblioteca Lora SX1262 con 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.oHacemos estos cambios ...
Cambiar riscv64-unknown-elf-gcc a zig cc
Agregue el Target -target riscv32-freestanding-none -mcpu=baseline_rv32-d
Eliminar -march=rv32imafc
Y corremos esto ...
# # 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
makeEl compilador de zig muestra estos errores ...
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];
^
Solucionamos esto incluyendo los archivos de encabezado correctos ...
#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__)En estos archivos de origen ...
(Ver los cambios)
Insertamos este código para decirnos (en tiempo de ejecución) si fue compilado con el compilador de zig o 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__(Fuente)
Compilado con zig cc , la biblioteca Lora SX1262 funciona bien en 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
(Ver el registro completo)
Compilemos la enorme biblioteca de Lorawan con el compilador de zig.
Nuttx compila la biblioteca de Lorawan como esta ...
# # 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.oCambiamos al compilador 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
makeIncluimos los archivos de encabezado correctos en 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__)(Ver los cambios)
Loramac.c compila OK con el compilador ZIG.
TODO: compile los otros archivos en la Biblioteca Lorawan con build.zig
https://ziglang.org/documentation/master/#zig-build-system
TODO: prueba la biblioteca de Lorawan
Ahora compilamos la aplicación Lorawan lorawan_test_main.c con el compilador ZIG.
Nuttx compila la aplicación Lorawan lorawan_test_main.c así ...
# # 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.oCambiamos al compilador de 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
makeIncluimos los archivos de encabezado correctos en 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__)(Ver los cambios)
Compilado con zig cc , la aplicación Lorawan funciona bien en 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
(Ver el registro completo)
El compilador ZIG puede traducir automáticamente el código C para zig. (Ver esto)
Así es como translacamos automáticamente nuestra aplicación Lorawan lorawan_test_main.c de c a zig ...
Cambiar zig cc a zig translate-c
Rodear las banderas C por -cflags ... --
Como esto...
# # 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.zigAquí está el código C original: Lorawan_Test_Main.c
Y la transentación automática de c a zig: traducido/lorawan_test_main.zig
Aquí hay un fragmento del código C original ...
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 ;
}(Fuente)
Y el código ZIG transluido automáticamente ...
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 ;
}(Fuente)
Nos referiremos a este código ZIG translgado automáticamente cuando convirtamos manualmente nuestra aplicación Lorawan Lorawan_Test_Main.c de C a ZIG en la siguiente sección ...
Pine64 Pinedio Stack BL604 (izquierda) Talking Lorawan a Rakwireless Wisgate (derecha)
Finalmente, convertimos la aplicación Lorawan lorawan_test_main.c de c a zig, para mostrar que podemos crear aplicaciones IoT complejas en zig.
La aplicación Lorawan se ejecuta en Pinedio Stack BL604 (RISC-V). La aplicación se conecta a una puerta de enlace de Lorawan (como Chirpstack o la red Things) y envía un paquete de datos a intervalos regulares.
Aquí está el código C original: Lorawan_Test_Main.c
(700 líneas de código C)
Y nuestra aplicación convertida de lorawan zig: lorawan_test.zig
(673 líneas de código 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 PacketPara compilar la aplicación 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
makeNuestra aplicación Lorawan Zig Lorawan_test.zig compila OK con Zig Compiler después de hacer las siguientes soluciones ...
Algunas partes de la aplicación Lorawan Zig lorawan_test.zig pueden ser difíciles de convertir de C a zig, como este código 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
);(Fuente)
Que tiene tipos contradictorios de firmado ( randr ) y sin firmar ( APP_TX_DUTYCYCLE ).
Obtuvimos ayuda refiriéndonos al código ZIG transluido automáticamente: Traducido/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
)
);Lo que resuelve los tipos conflictivos al lanzar el resultado firmado para que no se firme.
Cuando hacemos referencia LmHandlerCallbacks en nuestra aplicación Lorawan Zig Lorawan_test.zig ...
_ = & LmHandlerCallbacks ;El compilador ZIG mostrará este error de tipo opaco ...
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,
^
El error de tipo opaco se explica aquí ...
"Extienda un proyecto C/C ++ con zig"
"Fallos de traducción"
Rastrear nuestro error de tipo opaco ...
export fn OnMacMlmeRequest (
status : c.LoRaMacStatus_t ,
mlmeReq : [ * c ] c.MlmeReq_t ,
nextTxIn : c . TimerTime_t
) void {
c . DisplayMacMlmeRequestUpdate ( status , mlmeReq , nextTxIn );
} Nuestra función OnMacMlmeRequest tiene un parámetro de tipo MlmeReq_t , automático-importado por zig compiler como ...
pub const MlmeReq_t = struct_sMlmeReq ;
pub const struct_sMlmeReq = extern struct {
Type : Mlme_t ,
Req : union_uMlmeParam ,
ReqReturn : RequestReturnParam_t ,
}; Que contiene otro tipo de autoemportado union_uMlmeParam ...
pub const union_uMlmeParam = extern union {
Join : MlmeReqJoin_t ,
TxCw : MlmeReqTxCw_t ,
PingSlotInfo : MlmeReqPingSlotInfo_t ,
DeriveMcKEKey : MlmeReqDeriveMcKEKey_t ,
DeriveMcSessionKeyPair : MlmeReqDeriveMcSessionKeyPair_t ,
}; Que contiene un MlmeReqPingSlotInfo_t ...
pub const MlmeReqPingSlotInfo_t = struct_sMlmeReqPingSlotInfo ;
pub const struct_sMlmeReqPingSlotInfo = extern struct {
PingSlot : PingSlotInfo_t ,
}; Que contiene un PingSlotInfo_t ...
pub const PingSlotInfo_t = union_uPingSlotInfo ;
pub const union_uPingSlotInfo = extern union {
Value : u8 ,
Fields : struct_sInfoFields ,
}; Que contiene un struct_sInfoFields ...
pub const struct_sInfoFields = opaque {}; ¡Pero los campos de struct_sInfoFields no son conocidos por el compilador zig!
Si nos referimos al código C original ...
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 ;(Fuente)
Vemos que sInfoFields contiene campos de bits, que el compilador ZIG no puede traducir.
Anteriormente vimos que esto no se compila en nuestra aplicación Lorawan Zig Lorawan_test.zig ...
_ = & LmHandlerCallbacks ; Esto se debe a que LmHandlerCallbacks hace referencia al tipo Auto-Importado MlmeReq_t , que contiene campos de bits y no puede ser traducido por el compilador ZIG.
Convertamos MlmeReq_t a un tipo opaco, ya que no accederemos a los campos de todos modos ...
/// 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 {};(Fuente)
Convertimos LmHandlerCallbacks para usar nuestro tipo de opaco 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 ,
};(Fuente)
Cambiamos todas las referencias de MlmeReq_t automáticas de ...
[ * c ] c . MlmeReq_tA nuestro tipo opaco ...
* MlmeReq_t También cambiamos todas las referencias de LmHandlerCallbacks_t de Auto-Importada desde ...
[ * c ] c . LmHandlerCallbacks_t A nuestro convertido LmHandlerCallbacks_t ...
* LmHandlerCallbacks_tLo que significa que necesitamos importar las funciones de Lorawan afectadas nosotros mismos ...
/// 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 ;(Fuente)
Después de arreglar el tipo opaco, el compilador ZIG compila con éxito nuestra aplicación de prueba de Lorawan Lorawan_test.zig yay!
Al compilar nuestra aplicación de prueba de Lorawan lorawan_test.zig, vemos este error 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
Según los documentos de Zig, esto significa que el compilador ZIG no pudo traducir una macro C ...
Entonces nos definimos LL nosotros mismos ...
/// Import the LoRaWAN Library from C
const c = @cImport ({
// Workaround for "Unable to translate macro: undefined identifier `LL`"
@cDefine ( "LL" , "" ); ( LL es el sufijo "largo largo" para las constantes C, que probablemente no sea necesaria cuando importamos tipos y funciones en zig)
Entonces Zig Compiler emite este error ...
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
Que se refiere a esta línea en stdint.h ...
#define __int_c_join ( a , b ) a ## b La macro __int_c_join falla porque el sufijo LL ahora está en blanco y el operador de concatenación ## falla.
Redefinimos la macro __int_c_join sin el operador de concatenación ## ...
/// 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.hAhora Zig Compiler compila con éxito nuestra aplicación de prueba de Lorawan Lorawan_test.zig
El compilador de zig se bloquea cuando intenta inicializar la estructura del temporizador al inicio ...
/// 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(Fuente)
Así que inicializamos la estructura del temporizador en la función principal en su lugar ...
/// 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 );(Fuente)
Después de solucionar los problemas anteriores, probamos la aplicación Lorawan Zig en 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
(Ver el registro completo)
Lorawan Zig App Lorawan_test.zig se une con éxito a Lorawan Network (Chirpstack en Rakwireless Wisgate) y envía un paquete de datos a la puerta de enlace de Lorawan yay!
El compilador ZIG revela ideas interesantes al traducir automáticamente nuestro código C para zig.
Este código C copia una matriz, byte by byte ...
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
}(Fuente)
Aquí está el código ZIG de transcripción automática ...
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 ;
}(Fuente)
Tenga en cuenta que la indexación de matriz en c ...
// Array Indexing in C...
UnfragmentedData [ addr + i ]Se traduce a esto en zig ...
// Array Indexing in Zig...
UnfragmentedData [ addr +% i ] + en C se convierte en +% en zig!
¿Qué es +% en zig?
Ese es el operador ZIG para la adición de envoltura .
Lo que significa que el resultado retrocede a 0 (y más allá) si la adición desborda el entero.
Pero esto no es lo que pretendíamos, ya que no esperamos la adición de desbordamiento. Es por eso que en nuestro código ZIG final convertido final, volvemos +% a + ...
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
}(Fuente)
¿Qué sucede si la adición se desborda?
Veremos un error de tiempo de ejecución ...
panic: integer overflow
(Fuente)
Lo cual es probablemente algo bueno, para garantizar que nuestros valores sean sensibles.
¿Qué pasa si nuestro índice de matriz sale de los límites?
Recibiremos este error de tiempo de ejecución ...
panic: index out of bounds
(Fuente)
Aquí está la lista de cheques de seguridad realizados por zig en tiempo de ejecución ...
Si preferimos vivir imprudentemente, así es como deshabilitamos las verificaciones de seguridad ...
¿Algunas características de depuración no parecen estar funcionando? Como unreachable , std.debug.assert y std.debug.panic ?
Esto se debe a que para las plataformas integradas necesitamos implementar nuestro propio manejador de pánico ...
"Uso de zig para proporcionar trazas de pila en el pánico del núcleo para un sistema operativo de huesos"
Paniclador de pánico predeterminado: std.debug.default_panic
Con nuestro propio manejador de pánico, esta falla de afirmación ...
// Create a short alias named `assert`
const assert = std . debug . assert ;
// Assertion Failure
assert ( TxPeriodicity != 0 );Mostrará este rastro de pila ...
!ZIG PANIC!
reached unreachable code
Stack Trace:
0x23016394
0x23016ce0
Según nuestro desmontaje RISC-V, la primera dirección 23016394 no parece interesante, porque está dentro de la función 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>
Pero la segunda dirección 23016ce0 revela la afirmación que falló ...
/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
Esta es nuestra implementación del controlador de pánico 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 ) {}
}(Fuente)
Hemos implementado el registro de depuración std.log.debug que se describe aquí ...
Así es como llamamos std.log.debug para imprimir un mensaje de registro ...
// 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 .{ ... } Crea una estructura anónima con un número variable de argumentos que se pasarán a std.log.debug para imprimir.
A continuación se muestra nuestra implementación 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 );
}(Fuente)
Esta implementación llama puts() , que es compatible con Apache Nuttx RTOS, ya que es compatible con POSIX .
El código C original y el código ZIG convertido para nuestra aplicación Lorawan se ven muy similar.
Aquí está la función principal de nuestro código C original ...
/// 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 ;
}(Fuente)
Y la función principal de nuestro código ZIG convertido (después de un poco de fregado) ...
/// 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 ;
}(Fuente)
TODO: Limpie los nombres de tipos, funciones y variables
TODO: Lea el sensor de temperatura interna
TODO: Codifique los datos del sensor de temperatura con Tinycbor y transmitir a la red Things
https://lupyuen.github.io/articles/cbor2
TODO: Monitoree los datos del sensor con Prometheus y Grafana
https://lupyuen.github.io/articles/prometheus
TODO: Agregue el nuevo código con @import()
https://zig.news/mattnite/import-andpackages-23mb
TODO: ¿Necesitamos alinear los búferes con 32 bits al exportar a 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 );Reflexión de tipo zig ... ¿podemos usarlo para generar un gráfico de llamadas estructurado para bibliotecas C ... como para la biblioteca Lorawan? ?
Este programa ZIG importa la Biblioteca Lorawan de C y descarta los tipos y funciones 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
}(Fuente)
( @typeInfo se explica aquí)
Cuando el compilador zig compila el código anterior, vemos esto en el tiempo de compilación ...
$ 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);
^
(Fuente)
Que es una lista de tipos y funciones de C de la Biblioteca Lorawan.
Usemos esto para visualizar el gráfico de llamadas (por módulo) para la biblioteca Lorawan.
Presentaremos el gráfico de llamadas con sirena.js ...
https://mermaid-js.github.io/mermaid/#/./flowchart?id=flowcharts
Y agruparemos las funciones de Lorawan en el gráfico de llamadas por el módulo Lorawan (subgrafio), para que podamos ver las llamadas en los módulos Lorawan.
En el código anterior, ¿por qué usamos T2.Struct.decls[i].name en lugar 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 );
}
}Esperamos que este código imprima el nombre de la declaración ...
@compileLog ( "decl.name: " , decl . name );Como así ...
"decl.name: ", "LmnStatus_t"
Pero extrañamente imprime los bytes ...
"decl.name: ", []const u8{76,109,110,83,116,97,116,117,115,95,116}
El compilador de zig parece interpretar el nombre de manera diferente después de haber hecho referencia al nombre anteriormente ...
// If the C Declaration starts with "Lm" (LoRaMAC)...
if ( std . mem . startsWith ( u8 , decl . name , "Lm" )) { ...Entonces usamos esta solución alternativa ...
// Get a fresh reference to the Type Info
var T2 = @typeInfo ( c );
// This works OK
@compileLog ( "decl.name: " , T2 . Struct . decls [ i ]. name );Que produce el resultado que necesitamos ...
"decl.name: ", "LmnStatus_t"
¿Podemos identificar automáticamente el módulo Lorawan para cada función Lorawan, analizando las macros de encabezado HEADER_NAME_H__ C?
Intentemos esto para descargar todas las declaraciones de C y macros importadas 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(Fuente)
Obtenemos esta lista de funciones de Lorawan y macros 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__"
(Fuente)
Que no es útil. LmHandlerInit se declara dentro del archivo de encabezado C para __LORAMAC_HANDLER_H__ . Pero de alguna manera el tipo de reflexión en zig se mueve LmHandlerInit hasta la parte superior, antes de __LORAMAC_HANDLER_H__ aparece.
Por lo tanto, parece que necesitamos agrupar manualmente las funciones de Lorawan en los módulos Lorawan.
Las funciones de Lorawan parecen estar secuenciadas de acuerdo con el archivo de encabezado C, por lo que solo necesitamos etiquetar manualmente la primera función Lorawan para cada módulo Lorawan. Como esto...
LmHandlerInit → __LORAMAC_HANDLER_H__
LoRaMacInitialization → __LORAMAC_H__
RegionCommonValueInRange → __REGIONCOMMON_H__
SX126xInit → __SX126x_H__
SX126xIoInit → __SX126x_BOARD_H__
Nuestra aplicación ZIG puede hacer un tipo de reflexión sobre sí misma para descubrir sus tipos, constantes, variables y funciones ...
// 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" (Fuente)
Usaremos esto para trazar las llamadas de función desde nuestras funciones en zig a las funciones C en la Biblioteca Lorawan.
Anteriormente hemos capturado este registro de llamadas: un registro de llamadas a las funciones C en la Biblioteca 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
(Fuente)
Cada línea del registro de llamadas contiene el nombre de la función Lorawan. Combinaremos esto con la información de la reflexión de tipo zig para trazar el gráfico de llamadas.
Importamos el registro de llamadas en nuestra aplicación ZIG como ...
/// 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
...
;(Fuente)
El compilador ZIG puede procesar el registro de llamadas línea por línea como ...
// 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}(Fuente)
Para iterar a través de todas las líneas del registro de llamadas, hacemos esto ...
// 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(Fuente)
Hagamos que coincidamos con el registro de llamadas con los nombres de funciones de nuestra Biblioteca 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(Fuente)
El código anterior produce este resultado ...
| *"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"
...
(Fuente)
Que es una lista de funciones de Lorawan y la secuencia que se llamaron.
¡Un paso más cerca de representar nuestro gráfico de llamadas estructuradas!
Extendamos el código anterior y prestamos un gráfico ingenuo de llamadas ...
Para cada función de Lorawan que identificamos en el registro de llamadas ...
Trazamos una línea entre la función Lorawan y la siguiente función Lorawan en el registro de llamadas
Así que solo estamos trazando la secuencia conectada de llamadas de funciones
(¡Sí, es súper ingenuo!)
Lo hacemos así ...
// 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" , ";" );(Fuente)
El compilador de zig produce este resultado en el tiempo de compilación ...
| *"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", *";"
...
(Fuente)
Eliminamos manualmente los delimitadores del registro del compilador ZIG arriba como este ...
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;
...
Para obtener este diagrama de flujo de sirena. JS ...
diagrama de flujo TD;
Inicio-> loramacinitialización;
Loramacinitialización-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> SX126XIOInit;
Sx126xioinit-> sx126xsettx;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> timerInit;
TimerInit-> TimerInit;
TimerInit-> RadioSetModem;
RadioSetModem-> RadioSetModem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> radiosleep;
RadioSleep-> RadioSetModem;
RadioSetModem-> RadioSetPublicNetwork;
RadioSetPublicNetwork-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> TimerInit;
TimerInit-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimerInit;
TimerInit-> TimersetValue;
TimersetValue-> timerstop;
TimeStop-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> RadioStandby;
RadioostAndby-> radiosetchannel;
Radiosetchannel-> radiosetrxconfig;
RadioSetrxConfig-> RadioStandby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> RadioSetrxConfig;
Radiosetrxconfig-> radiorx;
RadiorX-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
Radioirqprocess-> irq_header_valid;
Irq_header_valid-> radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_rx_done;
Irq_rx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimeStop;
TimeStop-> lmhandlersend;
Lmhandlersend-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> RadioStandby;
RadioostAndby-> radiosetchannel;
Radiosetchannel-> radiosetrxconfig;
RadioSetrxConfig-> RadioStandby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> RadioSetrxConfig;
Radiosetrxconfig-> radiorx;
RadiorX-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
Radioirqprocess-> irq_rx_tx_timeout;
Irq_rx_tx_timeout-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> RadioStandby;
RadioostAndby-> radiosetchannel;
Radiosetchannel-> radiosetrxconfig;
RadioSetrxConfig-> RadioStandby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> RadioSetrxConfig;
Radiosetrxconfig-> radiorx;
RadiorX-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
Radioirqprocess-> irq_rx_tx_timeout;
Irq_rx_tx_timeout-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> RadioStandby;
RadioostAndby-> radiosetchannel;
Radiosetchannel-> radiosetrxconfig;
RadioSetrxConfig-> RadioStandby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> RadioSetrxConfig;
Radiosetrxconfig-> radiorx;
RadiorX-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
Radioirqprocess-> irq_rx_tx_timeout;
Irq_rx_tx_timeout-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> RadioStandby;
RadioostAndby-> radiosetchannel;
Radiosetchannel-> radiosetrxconfig;
RadioSetrxConfig-> RadioStandby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> RadioSetrxConfig;
Radiosetrxconfig-> radiorx;
RadiorX-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
Radioirqprocess-> irq_rx_tx_timeout;
Irq_rx_tx_timeout-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimeStop;
TimeStop-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> lmhandlersend;
Lmhandlersend-> radiosetchannel;
Radiosetchannel-> radiosettxconfig;
Radiosettxconfig-> radiosettxconfig;
RadiosettxConfig-> RadioTandyby;
RadioTandandby-> RadioSetModem;
RadioSetModem-> SX126XSETTX;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> radiosend;
Radiosend-> radiosend;
RadioSend-> TimeStop;
TimeStop-> Timerstart;
Timerstart-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> irq_tx_done;
Irq_tx_done-> timerstop;
TimeStop-> Radioondioirq;
Radioondioirq-> RadioirqProcess;
RadioirqProcess-> radiosleep;
RadioSleep-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
TimeStart-> TimersetValue;
TimersetValue-> Timerstart;
TimeSstart-> TimeStop;
TimeStop-> Timerstart;
¿Qué pasa con el gráfico de llamadas súper auténticas que hemos producido?
flowchart TD;
Start-->LoRaMacInitialization;
LoRaMacInitialization-->TimerInit;
TimerInit-->TimerInit;
TimerInit-->SX126xIoInit;
SX126xIoInit-->SX126xSetTx;
SX126xSetTx-->SX126xSetPaConfig;
SX126xSetPaConfig-->TimerInit;
TimerInit-->RadioSetModem;
...
(Fuente)
Nuestro gráfico de llamadas carece de estructura . Recuerde que solo estamos trazando la secuencia de llamadas de funciones ...
TimerInit → TimerInit no tiene sentido (porque no tenemos funciones recursivas)
TimerInit → RadioSetModem es totalmente incorrecto porque TimerInit es una función de bajo nivel (biblioteca ágil múltiple), mientras que RadioSetModem es una función de alto nivel (Biblioteca SX1262)
¿Podemos agregar alguna estructura para mejorar el gráfico de llamadas?
Para producir un gráfico de llamadas estructuradas, agruparemos las funciones en módulos, desde el nivel de alto nivel hasta ...
Aplicación ZIG (más alto nivel)
Biblioteca Lorawan
Biblioteca SX1262
Biblioteca ágil múltiple (nivel más bajo)
Y nos aseguraremos de que nunca dibujemos una línea de una función de bajo nivel a una función de alto nivel.
Este es un fragmento de lo que queremos lograr ...
diagrama de flujo TD;
Subgraph Lorawan;
Comenzar;
Loramacinitialización;
fin;
Subgraph SX1262;
Sx126xioinit;
SX126XSETTX;
Sx126xsetpaconfig;
fin;
subgraph ágil;
Temporizador;
Temporizador;
fin;
Inicio-> loramacinitialización;
Loramacinitialización-> TimerInit;
Sx126xioinit-> sx126xsettx;
Sx126xsettx-> sx126xsetpaconfig;
Sx126xsetpaconfig-> timerInit;
Agreguemos alguna estructura a nuestro gráfico de llamadas agrupando las funciones C en módulos.
Así es como definimos los módulos, de alto nivel a bajo nivel ...
// 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 ,
},
};(Fuente)
Tenga en cuenta que solo especificamos las primeras y últimas funciones para cada módulo.
Esto se debe a que las funciones del mismo módulo se agrupan en el tipo de reflexión en zig.
Luego renderizamos cada módulo como una subgraph de sirena.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
}
}(Fuente)
Cada subgraph contiene una lista de funciones que pertenecen al módulo ...
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;
...
(Fuente)
Cuando renderizamos la salida con Mermaid.js, obtenemos un gráfico de llamadas estructurado que parece más significativo ...
diagrama de flujo TD;
Subgraph Lmhandler;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
Lmhandlersend;
fin;
Subgraph Loramac;
Loramacinitialización;
fin;
radio subgraph;
Radiosetmodem;
Radiosetmodem;
RadioSetPublicNetwork;
Radios dormido;
Radiosetmodem;
RadioSetPublicNetwork;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
Radiosetmodem;
Radiosend;
Radiosend;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radiotandyby;
Radiosetchanal;
Radiosetrxconfig;
Radiotandyby;
Radiosetmodem;
Radiosetrxconfig;
Radiorx;
Radioirqprocess;
Radioirqprocess;
Radioirqprocess;
Radios dormido;
Radioirqprocess;
Radiosetchanal;
Radiosettxconfig;
Radiosettxconfig;
Radiotandyby;
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;
HACER
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
}
}(Fuente)
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
}
}(Fuente)
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; ")