5. Developing your project

5.1. Your application code

At the root of the repository is a directory named user. This is where your own source code will go. A default file named main.c is provided. If you need to add more source files to your project, you must declare them in the file CMakeLists.txt at the root of the repository: you will add your files to the list named LAZULI_USER_SOURCE_FILES.

5.2. Building

The next step is to build your project.

The Lazuli kernel itself is not executable. It is developed as a static library that your own program will link against at compile time. This is why the main function that you can see in the file main.c is the real main function of the final program.

The whole Lazuli project relies on CMake for configuration and compilation. As Lazuli is designed to be cross platform and configurable, we will explain here how to configure CMake to your specific project before building it.

5.2.1. Configuration

The first step is to make CMake generate the appropriate cross compilation toolchain for your project. This toolchain depends on the target platform.

We will do that in an interactive fashion using the tool ccmake. It is recommended that you perform these operations in the Lazuli container.

We first create and browse the directory build which is the destination directory of build artefacts, and we invoke cmake by pointing to the root CMakeFile.txt

[root@6bf01305461f workspace]# mkdir build && cd build
[root@6bf01305461f build]# cmake ..
CMake Error at sys/cmake/machine_choice.cmake:23 (message):
Configuration error: The target machine must be defined with cache variable
'LZ_TARGET_MACHINE_CHOICE'.
Call Stack (most recent call first):
sys/CMakeLists.txt:23 (include)


-- Configuring incomplete, errors occurred!
[root@6bf01305461f build]#

This results in an error because the build system doesn’t kow yet what is the target platform. We will fix this using ccmake, a console tool used to configure cmake cache:

[root@6bf01305461f build]# ccmake .

ccmake only displays one configuration variable, LZ_TARGET_MACHINE_CHOICE, by hitting ENTER (RETURN) will cycle through the supported platforms.

                                                    Page 1 of 1
LZ_TARGET_MACHINE_CHOICE

















LZ_TARGET_MACHINE_CHOICE: Choice of the target machine.
Keys: [enter] Edit an entry [d] Delete an entry             CMake Version 3.17.2
    [l] Show log output   [c] Configure
    [h] Help              [q] Quit without generating
    [t] Toggle advanced mode (currently off)

For now, only AVR_ATmega328p is supported.

                                                    Page 1 of 1
LZ_TARGET_MACHINE_CHOICE         AVR_ATmega328p

















LZ_TARGET_MACHINE_CHOICE: Choice of the target machine.
Keys: [enter] Edit an entry [d] Delete an entry             CMake Version 3.17.2
    [l] Show log output   [c] Configure
    [h] Help              [q] Quit without generating
    [t] Toggle advanced mode (currently off)

We can now ask CMake to configure the cross-compilation toolchain, hitting c:

The C compiler identification is GNU 9.2.0
The ASM compiler identification is GNU
Found assembler: /usr/bin/avr-gcc
Check for working C compiler: /usr/bin/avr-gcc
Check for working C compiler: /usr/bin/avr-gcc - works
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Detecting C compile features
Detecting C compile features - done
Using module: module_version_string
Configuring done








Configure produced the following output
                                                            CMake Version 3.17.2
Press [e] to exit screen

After exiting this screen by hitting e, ccmake now displays all the available configuration variables for the toolchain it created:

                                                    Page 1 of 3
AVR_AR                          */usr/bin/avr-ar
AVR_CC                          */usr/bin/avr-gcc
AVR_NM                          */usr/bin/avr-nm
AVR_OBJCOPY                     */usr/bin/avr-objcopy
AVR_OBJDUMP                     */usr/bin/avr-objdump
CMAKE_BUILD_TYPE                *
CMAKE_INSTALL_PREFIX            */usr/local
CMAKE_TOOLCHAIN_FILE            */~/workspace/sys/cmake/avr.toolchain.cmake
LZ_CONFIG_AVR_INSTRUMENT_PORT   *0x2B
LZ_CONFIG_BREAK_STACK_GAP       *50
LZ_CONFIG_CHECK_INTERRUPT_CODE  *ON
LZ_CONFIG_CHECK_NULL_PARAMETER  *ON
LZ_CONFIG_CHECK_NULL_PARAMETER  *ON
LZ_CONFIG_CHECK_NULL_PARAMETER  *ON
LZ_CONFIG_CHECK_NULL_PARAMETER  *ON
LZ_CONFIG_CHECK_WRONG_ENUM_ENT  *ON
LZ_CONFIG_DEFAULT_TASK_PRIORIT  *0

AVR_AR: Path to a program.
Keys: [enter] Edit an entry [d] Delete an entry             CMake Version 3.17.2
    [l] Show log output   [c] Configure
    [h] Help              [q] Quit without generating
    [t] Toggle advanced mode (currently off)

You can configure these variables according to your needs and hit c again:

Using module: module_version_string
Configuring done

















Configure produced the following output
                                                            CMake Version 3.17.2
Press [e] to exit screen

After hitting e, you can now generate the full configured toolchain and exit by hitting g. You can then return to the workspace:

[root@6bf01305461f build]# cd ..
[root@6bf01305461f workspace]#

Note that if you already know your target platform, the cross compilation toolchain can be generated by doing only:

[root@6bf01305461f workspace]# cmake -S . -B ./build -DLZ_TARGET_MACHINE_CHOICE=AVR_ATmega328p
-- The C compiler identification is GNU 9.2.0
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/avr-gcc
-- Check for working C compiler: /usr/bin/avr-gcc
-- Check for working C compiler: /usr/bin/avr-gcc - works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
Using module: module_version_string
-- Configuring done
-- Generating done
-- Build files have been written to: /~/workspace/build
[root@6bf01305461f workspace]#

At any moment you can set configuration variables by invoking ccmake pointing to the build directory:

[root@6bf01305461f workspace]# ccmake build/

Modules

5.2.2. Compilation

Compilation is quite straightforward. Simply invoke cmake with build option pointing to the build directory:

[root@6bf01305461f workspace]# cmake --build ./build
Scanning dependencies of target module_version_string
[  4%] Building C object sys/kern/modules/version_string/CMakeFiles/module_version_string.dir/version_string.c.obj
[  4%] Built target module_version_string
Scanning dependencies of target LazuliKernel_AVR_ATmega328p_0.1.0
[  9%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/arch/AVR/arch.c.obj
[ 13%] Building ASM object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/arch/AVR/interrupt_vectors_table.S.obj
[ 18%] Building ASM object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/arch/AVR/startup.S.obj
[ 22%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/arch/AVR/timer_counter_1.c.obj
[ 27%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/kernel.c.obj
[ 31%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/memory.c.obj
[ 36%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/scheduler.c.obj
[ 40%] Building C object sys/CMakeFiles/LazuliKernel_AVR_ATmega328p_0.1.0.dir/kern/list.c.obj
[ 45%] Linking C static library libLazuliKernel_AVR_ATmega328p_0.1.0.a
[ 45%] Built target LazuliKernel_AVR_ATmega328p_0.1.0
Scanning dependencies of target LazuliUserProject_AVR_ATmega328p_1.0.0
[ 50%] Building C object CMakeFiles/LazuliUserProject_AVR_ATmega328p_1.0.0.dir/user/main.c.obj
[ 54%] Linking C executable LazuliUserProject_AVR_ATmega328p_1.0.0
[ 54%] Built target LazuliUserProject_AVR_ATmega328p_1.0.0
Scanning dependencies of target hex_output
[ 59%] Generating user HEX file: LazuliUserProject_AVR_ATmega328p_1.0.0.hex
[ 59%] Built target hex_output
Scanning dependencies of target lst_ouput
[ 63%] Generating user LST file: LazuliUserProject_AVR_ATmega328p_1.0.0.lst
[ 63%] Built target lst_ouput
Scanning dependencies of target kernel_lst_ouput
[ 68%] Generating kernel LST file: LazuliKernel_AVR_ATmega328p_0.1.0.lst
[ 68%] Built target kernel_lst_ouput
Scanning dependencies of target module_clock_24
[ 72%] Building C object sys/kern/modules/clock_24/CMakeFiles/module_clock_24.dir/clock_24.c.obj
[ 72%] Built target module_clock_24
Scanning dependencies of target module_mutex
[ 77%] Building C object sys/kern/modules/mutex/CMakeFiles/module_mutex.dir/mutex.c.obj
[ 81%] Building ASM object sys/kern/modules/mutex/CMakeFiles/module_mutex.dir/arch/AVR/mutex.S.obj
[ 81%] Built target module_mutex
Scanning dependencies of target module_serial
[ 86%] Building C object sys/kern/modules/serial/CMakeFiles/module_serial.dir/serial.c.obj
[ 90%] Building C object sys/kern/modules/serial/CMakeFiles/module_serial.dir/arch/AVR/usart.c.obj
[ 90%] Built target module_serial
Scanning dependencies of target module_spinlock
[ 95%] Building C object sys/kern/modules/spinlock/CMakeFiles/module_spinlock.dir/spinlock.c.obj
[100%] Building ASM object sys/kern/modules/spinlock/CMakeFiles/module_spinlock.dir/arch/AVR/spinlock.S.obj
[100%] Built target module_spinlock
[root@6bf01305461f workspace]#

You can notice that the build system builds all modules, even those that are not used by your project. However they are built but not linked to your project. This is to ensure that code modifications made to the kernel doesn’t break integrity of the whole kernel, including modules.