1
0
Fork 0

Add support for hardware and board initialisation overrides. (#8330)

* Add support for hardware and board initialisation overrides.

* qmk cformat.

* Add some documentation.

* Docs clarity.

* Make early_hardware_init_pre a no-op for now, until migrations occur.

* Doco update

* Make distinction between keyboard and ChibiOS board in docs

* Doc anchors.

* Update tmk_core/protocol/chibios/main.c

Co-Authored-By: Joel Challis <git@zvecr.com>

* Rework bootloader entry to be off by default, allow opting-in.

Co-authored-by: Joel Challis <git@zvecr.com>
This commit is contained in:
Nick Brassel 2020-04-13 09:39:38 +10:00 committed by GitHub
parent 17bda000f3
commit 05e9ff6554
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 5 deletions

View file

@ -136,6 +136,10 @@
* [Development Environment](api_development_environment.md) * [Development Environment](api_development_environment.md)
* [Architecture Overview](api_development_overview.md) * [Architecture Overview](api_development_overview.md)
* Hardware Platform Development
* Arm/ChibiOS
* [Early initialization](platformdev_chibios_earlyinit.md)
* QMK Reference * QMK Reference
* [Contributing to QMK](contributing.md) * [Contributing to QMK](contributing.md)
* [Translating the QMK Docs](translating.md) * [Translating the QMK Docs](translating.md)

View file

@ -0,0 +1,53 @@
# Arm/ChibiOS Early Initialization :id=chibios-early-init
This page describes a part of QMK that is a somewhat advanced concept, and is only relevant to keyboard designers.
QMK uses ChibiOS as the underlying layer to support a multitude of Arm-based devices. Each ChibiOS-supported keyboard has a low-level board definition which is responsible for initializing hardware peripherals such as the clocks, and GPIOs.
Older QMK revisions required duplication of these board definitions inside your keyboard's directory in order to override such early initialization points; this is now abstracted into the following APIs, and allows usage of the board definitions supplied with ChibiOS itself. Check `<qmk_firmware>/lib/chibios/os/hal/boards` for the list of official definitions. If your keyboard needs extra initialization at a very early stage, consider providing keyboard-level overrides of the following APIs:
## `early_hardware_init_pre()` :id=early-hardware-init-pre
The function `early_hardware_init_pre` is the earliest possible code that can be executed by a keyboard firmware. This is intended as a replacement for the ChibiOS board definition's `__early_init` function, and is the equivalent of executing at the start of the function.
This is executed before RAM gets cleared, and before clocks or GPIOs are configured; any delays or preparation using GPIOs is not likely to work at this point. After executing this function, RAM on the MCU may be zero'ed. Assigning values to variables during execution of this function may be overwritten.
As such, if you wish to override this API consider limiting use to writing to low-level registers. The default implementation of this function can be configured to jump to bootloader if a `RESET` key was pressed, by ensuring `#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE` is in the keyboard's `config.h` file.
To implement your own version of this function, in your keyboard's source files:
```c
void early_hardware_init_pre(void) {
// do things with registers
}
```
## `early_hardware_init_post()` :id=early-hardware-init-post
The function `early_hardware_init_post` is the next earliest possible code that can be executed by a keyboard firmware. This is executed after RAM has been cleared, and clocks and GPIOs are configured. This is intended as a replacement for the ChibiOS board definition's `__early_init` function, and is the equivalent of executing at the end of the function.
Much like `early_hardware_init_pre`, ChibiOS has not yet been initialized either, so the same restrictions on delays and timing apply.
If you wish to override this API, consider limiting functionality to register writes, variable initialization, and GPIO toggling. The default implementation of this function is to do nothing.
To implement your own version of this function, in your keyboard's source files:
```c
void early_hardware_init_post(void) {
// toggle GPIO pins and write to variables
}
```
## `board_init()` :id=board-init
The function `board_init` is executed directly after the ChibiOS initialization routines have completed. At this stage, all normal low-level functionality should be available for use (including timers and delays), with the restriction that USB is not yet connected. This is intended as a replacement for the ChibiOS board definition's `boardInit` function.
The default implementation of this function is to do nothing.
To implement your own version of this function, in your keyboard's source files:
```c
void board_init(void) {
// initialize anything that requires ChibiOS
}
```

View file

@ -0,0 +1,5 @@
#pragma once
// Override the initialisation functions inside the ChibiOS board.c files
#define __early_init __chibios_override___early_init
#define boardInit __chibios_override_boardInit

View file

@ -33,6 +33,11 @@
#include "debug.h" #include "debug.h"
#include "printf.h" #include "printf.h"
#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
// Change this to be TRUE once we've migrated keyboards to the new init system
# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE
#endif
#ifdef SLEEP_LED_ENABLE #ifdef SLEEP_LED_ENABLE
# include "sleep_led.h" # include "sleep_led.h"
#endif #endif
@ -101,6 +106,39 @@ void midi_ep_task(void);
// } // }
// } // }
/* Early initialisation
*/
__attribute__((weak)) void early_hardware_init_pre(void) {
#if EARLY_INIT_PERFORM_BOOTLOADER_JUMP
void enter_bootloader_mode_if_requested(void);
enter_bootloader_mode_if_requested();
#endif // EARLY_INIT_PERFORM_BOOTLOADER_JUMP
}
__attribute__((weak)) void early_hardware_init_post(void) {}
__attribute__((weak)) void board_init(void) {}
// This overrides what's normally in ChibiOS board definitions
void __early_init(void) {
early_hardware_init_pre();
// This is the renamed equivalent of __early_init in the board.c file
void __chibios_override___early_init(void);
__chibios_override___early_init();
early_hardware_init_post();
}
// This overrides what's normally in ChibiOS board definitions
void boardInit(void) {
// This is the renamed equivalent of boardInit in the board.c file
void __chibios_override_boardInit(void);
__chibios_override_boardInit();
board_init();
}
/* Main thread /* Main thread
*/ */
int main(void) { int main(void) {

View file

@ -223,6 +223,12 @@ $(foreach LOBJ, $(NO_LTO_OBJ), $(eval $(call NO_LTO,$(LOBJ))))
MOVE_DEP = mv -f $(patsubst %.o,%.td,$@) $(patsubst %.o,%.d,$@) MOVE_DEP = mv -f $(patsubst %.o,%.td,$@) $(patsubst %.o,%.d,$@)
# For a ChibiOS build, ensure that the board files have the hook overrides injected
define BOARDSRC_INJECT_HOOKS
$(KEYBOARD_OUTPUT)/$(patsubst %.c,%.o,$(patsubst ./%,%,$1)): INIT_HOOK_CFLAGS += -include $(TOP_DIR)/tmk_core/protocol/chibios/init_hooks.h
endef
$(foreach LOBJ, $(BOARDSRC), $(eval $(call BOARDSRC_INJECT_HOOKS,$(LOBJ))))
# Add QMK specific flags # Add QMK specific flags
DFU_SUFFIX ?= dfu-suffix DFU_SUFFIX ?= dfu-suffix
DFU_SUFFIX_ARGS ?= DFU_SUFFIX_ARGS ?=
@ -306,27 +312,27 @@ ifdef $1_CONFIG
$1_CONFIG_FLAGS += $$(patsubst %,-include %,$$($1_CONFIG)) $1_CONFIG_FLAGS += $$(patsubst %,-include %,$$($1_CONFIG))
endif endif
$1_CFLAGS = $$(ALL_CFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS) $1_CFLAGS = $$(ALL_CFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS)
$1_CXXFLAGS= $$(ALL_CXXFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS) $1_CXXFLAGS = $$(ALL_CXXFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS)
$1_ASFLAGS= $$(ALL_ASFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $1_ASFLAGS = $$(ALL_ASFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS)
# Compile: create object files from C source files. # Compile: create object files from C source files.
$1/%.o : %.c $1/%.d $1/cflags.txt $1/compiler.txt | $(BEGIN) $1/%.o : %.c $1/%.d $1/cflags.txt $1/compiler.txt | $(BEGIN)
@mkdir -p $$(@D) @mkdir -p $$(@D)
@$$(SILENT) || printf "$$(MSG_COMPILING) $$<" | $$(AWK_CMD) @$$(SILENT) || printf "$$(MSG_COMPILING) $$<" | $$(AWK_CMD)
$$(eval CMD := $$(CC) -c $$($1_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP)) $$(eval CMD := $$(CC) -c $$($1_CFLAGS) $$(INIT_HOOK_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))
@$$(BUILD_CMD) @$$(BUILD_CMD)
# Compile: create object files from C++ source files. # Compile: create object files from C++ source files.
$1/%.o : %.cpp $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN) $1/%.o : %.cpp $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN)
@mkdir -p $$(@D) @mkdir -p $$(@D)
@$$(SILENT) || printf "$$(MSG_COMPILING_CXX) $$<" | $$(AWK_CMD) @$$(SILENT) || printf "$$(MSG_COMPILING_CXX) $$<" | $$(AWK_CMD)
$$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP)) $$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(INIT_HOOK_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))
@$$(BUILD_CMD) @$$(BUILD_CMD)
$1/%.o : %.cc $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN) $1/%.o : %.cc $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN)
@mkdir -p $$(@D) @mkdir -p $$(@D)
@$$(SILENT) || printf "$$(MSG_COMPILING_CXX) $$<" | $$(AWK_CMD) @$$(SILENT) || printf "$$(MSG_COMPILING_CXX) $$<" | $$(AWK_CMD)
$$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP)) $$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(INIT_HOOK_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))
@$$(BUILD_CMD) @$$(BUILD_CMD)
# Assemble: create object files from assembler source files. # Assemble: create object files from assembler source files.