Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
acaf57a
refactor: split header-only drivers into source + header files
Mar 4, 2026
b8dc669
fix: add missing declarations for fan_state and REGISTER_INTERRUPT
Mar 5, 2026
d035e0a
fix: resolve compilation errors after header/source split
Mar 5, 2026
b03d594
fix: update SConscript to compile driver .c files and add missing dec…
Mar 5, 2026
b52dbba
fix: add drivers.h includes for type definitions
Mar 5, 2026
3f7e548
fix: use separate object directories for bootstub and main driver builds
Mar 5, 2026
13db127
fix: add uart_ring type to drivers.h for BOOTSTUB, fix fdcan.h includes
Mar 5, 2026
10dbfb3
fix: include drivers.h before uart_ring_som_debug definition in BOOTS…
Mar 5, 2026
1c2b550
fix: remove duplicate struct definitions from harness.h and uart.h
Mar 5, 2026
a104951
fix: remove duplicate fan_state_t definition from fan.h
Mar 5, 2026
d029671
fix: resolve compilation errors in can_comms.h and sound.h
Mar 7, 2026
f3f4c7a
fix: add forward declaration for refresh_can_tx_slots_available
Mar 7, 2026
3f3db7c
fix: include can_common.h for can_set_checksum declaration
Mar 7, 2026
2a93ec3
fix: correct include paths in driver source files
Mar 7, 2026
d7b5b78
fix: add stdint.h and stdbool.h includes to health.h
Mar 7, 2026
1ebc07a
fix: include board/config.h in all driver source files
Mar 8, 2026
5360c4f
chore: trigger CI rebuild
Mar 8, 2026
fec69c1
fix: add missing includes and declarations in drivers.h
Mar 8, 2026
ece42c2
fix: extract BootState to separate header and add missing declaration
Mar 8, 2026
221bcb0
fix: extract BootState to separate header to avoid circular deps
Mar 8, 2026
1bfd271
fix: remove duplicate board/boot_state.h
Mar 8, 2026
fe4bfb8
fix: conditionally compile bootkick.c + add safety.h include
Mar 8, 2026
7922421
fix: fake_siren.c needs sound.h for sound_stop_dac
Mar 8, 2026
aae7e03
fix: guard bootkick declarations for non-regular panda builds
Mar 8, 2026
d132ce7
fix: exclude fan/fake_siren/harness from body/jungle builds
Mar 8, 2026
2168c45
fix: remove duplicate interrupt defs from drivers.h (already in inter…
Mar 8, 2026
17aa60c
fix: add missing #endif for STM32H7 block in drivers.h
Mar 8, 2026
b42f6d8
fix: add missing struct interrupt and related declarations to drivers.h
Mar 8, 2026
2acfc74
fix: remove duplicate struct interrupt from interrupts.h
Mar 8, 2026
911ba18
fix: declare 'time' variable in handle_interrupt
Mar 8, 2026
bc51abc
fix: add extern declaration for uart_ring_som_debug in bootkick.c
Mar 8, 2026
7b61913
fix: add extern declarations for uart_ring_debug and uart_ring_som_debug
Mar 8, 2026
34b71f7
fix: add llfdcan includes to fdcan.c
Mar 8, 2026
a602521
fix: remove implementations from interrupts.h - keep only declarations
Mar 8, 2026
952e8de
fix: wrap uart.c content in #ifndef BOOTSTUB
Mar 8, 2026
da42085
fix: remove direct llfdcan.h include from fdcan.c
Mar 8, 2026
cac2428
fix: correct BUS_NUM_FROM_CAN_NUM typo in fdcan.c
Mar 8, 2026
7996fd4
fix: add extern declarations for CAN ring buffers
Mar 8, 2026
f0ab879
fix: include safety.h in fdcan.c for safety_fwd_hook
Mar 8, 2026
790aa98
fix: include led.h in fdcan.c for led_set
Mar 8, 2026
40fbd00
fix: remove redundant sound.h include from fake_siren.c
Mar 8, 2026
42f4e36
fix: resolve multiple definition errors when drivers compiled separately
Mar 8, 2026
d11e12f
fix: use opendbc.DBC_PATH instead of opendbc.INCLUDE_PATH
Mar 8, 2026
853083d
fix: resolve enable_interrupts static/non-static declaration conflict
Mar 8, 2026
aad55bb
fix: remove unnecessary can.h include from sys.c
Mar 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def build_project(project_name, project, main, extra_flags):
CFLAGS=flags,
ASFLAGS=flags,
LINKFLAGS=flags,
CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.INCLUDE_PATH],
CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.DBC_PATH],
ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES",
BUILDERS={
'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf')
Expand All @@ -100,22 +100,51 @@ def build_project(project_name, project, main, extra_flags):

startup = env.Object(project["STARTUP_FILE"])

# Driver source files (split from header-only)
driver_sources = [
"./board/drivers/bootkick.c",
"./board/drivers/can_common.c",
"./board/drivers/clock_source.c",
"./board/drivers/fake_siren.c",
"./board/drivers/fan.c",
"./board/drivers/fdcan.c",
"./board/drivers/gpio.c",
"./board/drivers/harness.c",
"./board/drivers/interrupts.c",
"./board/drivers/led.c",
"./board/drivers/pwm.c",
"./board/drivers/registers.c",
"./board/drivers/simple_watchdog.c",
"./board/drivers/spi.c",
"./board/drivers/sys.c",
"./board/drivers/timers.c",
"./board/drivers/uart.c",
"./board/drivers/usb.c",
]

# bootkick is only for regular panda (has SOM support), not body/jungle
if "-DPANDA_BODY" in extra_flags or "-DPANDA_JUNGLE" in extra_flags:
driver_sources = [s for s in driver_sources if s not in ["./board/drivers/bootkick.c", "./board/drivers/fan.c", "./board/drivers/fake_siren.c", "./board/drivers/harness.c"]]
# Build bootstub
bs_env = env.Clone()
bs_env.Append(CFLAGS="-DBOOTSTUB", ASFLAGS="-DBOOTSTUB", LINKFLAGS="-DBOOTSTUB")
# Use separate object directory for bootstub driver objects
bs_env['OBJPREFIX'] = Dir(f'./board/obj/{project_name}/bootstub/')
bs_driver_objs = bs_env.Object(driver_sources)
bs_elf = bs_env.Program(f"{project_dir}/bootstub.elf", [
startup,
"./board/crypto/rsa.c",
"./board/crypto/sha.c",
"./board/bootstub.c",
])
] + bs_driver_objs)
bs_env.Objcopy(f"./board/obj/bootstub.{project_name}.bin", bs_elf)

# Build + sign main (aka app)
main_driver_objs = env.Object(driver_sources)
main_elf = env.Program(f"{project_dir}/main.elf", [
startup,
main
], LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
] + main_driver_objs, LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags)
main_bin = env.Objcopy(f"{project_dir}/main.bin", main_elf)
sign_py = File(f"./board/crypto/sign.py").srcnode().relpath
env.Command(f"./board/obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}")
Expand Down
6 changes: 1 addition & 5 deletions board/boards/board_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
#include <stdbool.h>

// ******************** Prototypes ********************
typedef enum {
BOOT_STANDBY,
BOOT_BOOTKICK,
BOOT_RESET,
} BootState;
#include "board/drivers/boot_state.h"

typedef void (*board_init)(void);
typedef void (*board_init_bootloader)(void);
Expand Down
9 changes: 3 additions & 6 deletions board/body/boards/board_body.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "board/body/motor_control.h"

void board_body_init(void) {
static inline void board_body_init(void) {
motor_init();
motor_encoder_init();
motor_speed_controller_init();
Expand All @@ -14,8 +14,5 @@ void board_body_init(void) {
set_gpio_alternate(GPIOD, 1, GPIO_AF9_FDCAN1);
}

board board_body = {
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {7, 7, 7},
.init = board_body_init,
};
// board_body is defined in board/body/main.c
extern board board_body;
7 changes: 7 additions & 0 deletions board/body/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

extern int _app_start[0xc000];

#include "board/body/boards/board_body.h"
board board_body = {
.led_GPIO = {GPIOC, GPIOC, GPIOC},
.led_pin = {7, 7, 7},
.init = board_body_init,
};

#include "board/body/main_comms.h"

void debug_ring_callback(uart_ring *ring) {
Expand Down
21 changes: 21 additions & 0 deletions board/bootstub.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,27 @@
#include "board/drivers/pwm.h"
#include "board/drivers/usb.h"

// Globals for BOOTSTUB mode
uint8_t hw_type = 0;
board *current_board;

// UART ring buffers for BOOTSTUB mode (uart.c not compiled in BOOTSTUB)
static uint8_t elems_rx_som_debug[FIFO_SIZE_INT];
static uint8_t elems_tx_som_debug[FIFO_SIZE_INT];
uart_ring uart_ring_som_debug = {
.w_ptr_tx = 0,
.r_ptr_tx = 0,
.elems_tx = ((uint8_t *)&(elems_tx_som_debug)),
.tx_fifo_size = FIFO_SIZE_INT,
.w_ptr_rx = 0,
.r_ptr_rx = 0,
.elems_rx = ((uint8_t *)&(elems_rx_som_debug)),
.rx_fifo_size = FIFO_SIZE_INT,
.uart = UART7,
.callback = NULL,
.overwrite = true
};

#include "board/early_init.h"
#include "board/provision.h"

Expand Down
5 changes: 3 additions & 2 deletions board/bootstub_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ typedef struct uart_ring uart_ring;
void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); }

// ********************* Globals **********************
uint8_t hw_type = 0;
board *current_board;
// hw_type and current_board are defined in bootstub.c
extern uint8_t hw_type;
extern board *current_board;
6 changes: 6 additions & 0 deletions board/can_comms.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
which is sent by the host on each start of a connection.
*/

// External CAN RX queue defined in drivers/can_common.c
extern can_ring can_rx_q;

// Forward declaration
void refresh_can_tx_slots_available(void);

typedef struct {
uint32_t ptr;
uint32_t tail_size;
Expand Down
12 changes: 12 additions & 0 deletions board/drivers/boot_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef DRIVERS_BOOT_STATE_H
#define DRIVERS_BOOT_STATE_H

// Boot state enum used by bootkick driver
// This is a minimal header to avoid circular dependencies
typedef enum {
BOOT_STANDBY,
BOOT_BOOTKICK,
BOOT_RESET,
} BootState;

#endif
79 changes: 79 additions & 0 deletions board/drivers/bootkick.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "board/config.h"

// bootkick is only used on regular panda, not on BODY or JUNGLE variants
#if !defined(PANDA_BODY) && !defined(PANDA_JUNGLE)

#include "board/drivers/bootkick.h"
#include "board/drivers/drivers.h"

// uart_ring_som_debug is defined in stm32h7_config.h for BOOTSTUB, or uart.c for main
extern uart_ring uart_ring_som_debug;

bool bootkick_reset_triggered = false;

void bootkick_tick(bool ignition, bool recent_heartbeat) {
static uint16_t bootkick_last_serial_ptr = 0;
static uint8_t waiting_to_boot_countdown = 0;
static uint8_t boot_reset_countdown = 0;
static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC;
static bool bootkick_ign_prev = false;
static BootState boot_state = BOOT_BOOTKICK;
BootState boot_state_prev = boot_state;
const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC);

if ((ignition && !bootkick_ign_prev) || harness_inserted) {
// bootkick on rising edge of ignition or harness insertion
boot_state = BOOT_BOOTKICK;
} else if (recent_heartbeat) {
// disable bootkick once openpilot is up
boot_state = BOOT_STANDBY;
} else {

}

/*
Ensure SOM boots in case it goes into QDL mode. Reset behavior:
* shouldn't trigger on the first boot after power-on
* only try reset once per bootkick, i.e. don't keep trying until booted
* only try once per panda boot, since openpilot will reset panda on startup
* once BOOT_RESET is triggered, it stays until countdown is finished
*/
if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) {
waiting_to_boot_countdown = 20U;
}
if (waiting_to_boot_countdown > 0U) {
bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr;
if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) {
waiting_to_boot_countdown = 0U;
} else {
// try a reset
if (waiting_to_boot_countdown == 1U) {
boot_reset_countdown = 5U;
}
}
}

// handle reset state
if (boot_reset_countdown > 0U) {
boot_state = BOOT_RESET;
bootkick_reset_triggered = true;
} else {
if (boot_state == BOOT_RESET) {
boot_state = BOOT_BOOTKICK;
}
}

// update state
bootkick_ign_prev = ignition;
bootkick_harness_status_prev = harness.status;
bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx;
if (waiting_to_boot_countdown > 0U) {
waiting_to_boot_countdown--;
}
if (boot_reset_countdown > 0U) {
boot_reset_countdown--;
}
current_board->set_bootkick(boot_state);
}

#endif // !PANDA_BODY && !PANDA_JUNGLE
70 changes: 6 additions & 64 deletions board/drivers/bootkick.h
Original file line number Diff line number Diff line change
@@ -1,68 +1,10 @@
#include "board/drivers/drivers.h"
#ifndef DRIVERS_BOOTKICK_H
#define DRIVERS_BOOTKICK_H

bool bootkick_reset_triggered = false;
#include <stdbool.h>

void bootkick_tick(bool ignition, bool recent_heartbeat) {
static uint16_t bootkick_last_serial_ptr = 0;
static uint8_t waiting_to_boot_countdown = 0;
static uint8_t boot_reset_countdown = 0;
static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC;
static bool bootkick_ign_prev = false;
static BootState boot_state = BOOT_BOOTKICK;
BootState boot_state_prev = boot_state;
const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC);
extern bool bootkick_reset_triggered;

if ((ignition && !bootkick_ign_prev) || harness_inserted) {
// bootkick on rising edge of ignition or harness insertion
boot_state = BOOT_BOOTKICK;
} else if (recent_heartbeat) {
// disable bootkick once openpilot is up
boot_state = BOOT_STANDBY;
} else {
void bootkick_tick(bool ignition, bool recent_heartbeat);

}

/*
Ensure SOM boots in case it goes into QDL mode. Reset behavior:
* shouldn't trigger on the first boot after power-on
* only try reset once per bootkick, i.e. don't keep trying until booted
* only try once per panda boot, since openpilot will reset panda on startup
* once BOOT_RESET is triggered, it stays until countdown is finished
*/
if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) {
waiting_to_boot_countdown = 20U;
}
if (waiting_to_boot_countdown > 0U) {
bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr;
if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) {
waiting_to_boot_countdown = 0U;
} else {
// try a reset
if (waiting_to_boot_countdown == 1U) {
boot_reset_countdown = 5U;
}
}
}

// handle reset state
if (boot_reset_countdown > 0U) {
boot_state = BOOT_RESET;
bootkick_reset_triggered = true;
} else {
if (boot_state == BOOT_RESET) {
boot_state = BOOT_BOOTKICK;
}
}

// update state
bootkick_ign_prev = ignition;
bootkick_harness_status_prev = harness.status;
bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx;
if (waiting_to_boot_countdown > 0U) {
waiting_to_boot_countdown--;
}
if (boot_reset_countdown > 0U) {
boot_reset_countdown--;
}
current_board->set_bootkick(boot_state);
}
#endif
Loading
Loading