klipper-dgus/docs/Code_Overview.md

5.0 KiB

This document describes the overall code layout and major code flow of Klipper.

Directory Layout

The src/ directory contains the C source for the micro-controller code. The src/avr/ directory contains specific code for Atmel ATmega micro-controllers. The src/sam3x8e/ directory contains code specific to the Arduino Due style ARM micro-controllers. The src/simulator/ contains code stubs that allow the micro-controller to be test compiled on other architectures. The src/generic/ directory contains helper code that may be useful across different host architectures. The build arranges for includes of "board/somefile.h" to first look in the current architecture directory (eg, src/avr/somefile.h) and then in the generic directory (eg, src/generic/somefile.h).

The klippy/ directory contains the C and Python source for the host part of the firmware.

The lib/ directory contains external 3rd-party library code that is necessary to build some targets.

The config/ directory contains example printer configuration files.

The scripts/ directory contains build-time scripts useful for compiling the micro-controller code.

During compilation, the build may create an out/ directory. This contains temporary build time objects. The final micro-controller object that is built is out/klipper.elf.hex on AVR and out/klipper.bin on ARM.

Micro-controller code flow

Execution of the micro-controller code starts in architecture specific code (eg, src/avr/main.c) which ultimately calls sched_main() located in src/sched.c. The sched_main() code starts by running all functions that have been tagged with the DECL_INIT() macro. It then goes on to repeatedly run all functions tagged with the DECL_TASK() macro.

One of the main task functions is command_task() located in src/command.c. This function processes incoming serial commands and runs the associated command function for them. Command functions are declared using the DECL_COMMAND() macro.

Task, init, and command functions always run with interrupts enabled (however, they can temporarily disable interrupts if needed). These functions should never pause, delay, or do any work that lasts more than a few micro-seconds. These functions schedule work at specific times by scheduling timers.

Timer functions are scheduled by calling sched_add_timer() (located in src/sched.c). The scheduler code will arrange for the given function to be called at the requested clock time. Timer interrupts are initially handled in an architecture specific interrupt handler (eg, src/avr/timer.c), but this just calls sched_timer_kick() located in src/sched.c. The timer interrupt leads to execution of schedule timer functions. Timer functions always run with interrupts disabled. The timer functions should always complete within a few micro-seconds. At completion of the timer event, the function may choose to reschedule itself.

In the event an error is detected the code can invoke shutdown() (a macro which calls sched_shutdown() located in src/sched.c). Invoking shutdown() causes all functions tagged with the DECL_SHUTDOWN() macro to be run. Shutdown functions always run with interrupts disabled.

Much of the functionality of the micro-controller involves working with General-Purpose Input/Output pins (GPIO). In order to abstract the low-level architecture specific code from the high-level task code, all GPIO events are implemented in architectures specific wrappers (eg, src/avr/gpio.c). The code is compiled with gcc's "-flto -fwhole-program" optimization which does an excellent job of inlining functions across compilation units, so most of these tiny gpio functions are inlined into their callers, and there is no run-time cost to using them.

Klippy code overview

The host code (Klippy) is intended to run on a low-cost computer (such as a Raspberry Pi) paired with the micro-controller. The code is primarily written in Python, however it does use CFFI to implement some functionality in C code.

Initial execution starts in klippy/klippy.py. This reads the command-line arguments, opens the printer config file, instantiates the main printer objects, and starts the serial connection. The main execution of gcode commands is in the process_commands() method in klippy/gcode.py. This code translates the gcode commands into printer object calls, which frequently translate the actions to commands to be executed on the micro-controller (as declared via the DECL_COMMAND macro in the micro-controller code).

There are four threads in the Klippy host code. The main thread handles incoming gcode commands. A second thread (which resides entirely in the klippy/serialqueue.c C code) handles low-level IO with the serial port. The third thread is used to process response messages from the micro-controller in the Python code (see klippy/serialhdl.py). The fourth thread writes debug messages to the log (see klippy/queuelogger.py) so that the other threads never block on log writes.