This commit is contained in:
infiniteCable2
2026-02-28 11:29:00 +01:00
60 changed files with 775 additions and 670 deletions

View File

@@ -44,18 +44,28 @@ jobs:
- uses: actions/checkout@v4
- run: ./test.sh
windows:
name: windows pip package test
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- name: Install package with uv
run: uv pip install --system .
- name: Verify importing panda
run: python -c "from panda import Panda"
misra_linter:
name: MISRA C:2012 Linter
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Cache cppcheck
id: cppcheck-cache
uses: actions/cache@v4
with:
path: tests/misra/cppcheck
key: cppcheck-${{ hashFiles('tests/misra/*') }}
- run: ./setup.sh
- name: Build FW
run: ${{ env.RUN }} "scons -j$(nproc)"
@@ -68,16 +78,8 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Cache cppcheck
id: cppcheck-cache
uses: actions/cache@v4
with:
path: tests/misra/cppcheck
key: cppcheck-${{ hashFiles('tests/misra/*') }}
- run: ./setup.sh
- name: Build FW
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: tests/misra/install.sh
run: ${{ env.RUN }} "cd tests/misra && ./install.sh"
- name: MISRA mutation tests
run: ${{ env.RUN }} "cd tests/misra && pytest test_mutation.py"

View File

@@ -1,41 +0,0 @@
name: Update cppcheck
on:
#push:
schedule:
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
workflow_dispatch:
jobs:
update-cppcheck:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Get latest cppcheck version
id: version
run: |
# Tags are sorted by time (newest first), so get the first version-like tag
LATEST=$(curl -fsSL "https://api.github.com/repos/danmar/cppcheck/tags?per_page=20" | \
jq -r '.[].name' | \
grep -E '^[0-9]+\.[0-9]+(\.[0-9]+)?$' | \
head -n 1)
echo "vers=$LATEST" >> "$GITHUB_OUTPUT"
- name: Update VERS in install.sh
run: |
sed -i "s/^VERS=\".*\"/VERS=\"${{ steps.version.outputs.vers }}\"/" tests/misra/install.sh
grep VERS tests/misra/install.sh
- name: Create Pull Request
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
with:
author: Vehicle Researcher <user@comma.ai>
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
commit-message: "[bot] Update cppcheck to ${{ steps.version.outputs.vers }}"
title: "[bot] Update cppcheck to ${{ steps.version.outputs.vers }}"
body: "See all cppcheck releases: https://github.com/danmar/cppcheck/releases"
branch: "update-cppcheck"
base: "master"
delete-branch: true
labels: bot

3
.gitignore vendored
View File

@@ -37,3 +37,6 @@ tests/safety/coverage.info
*.profraw
*.profdata
mull.yml
.claude/
TASK.md

View File

@@ -8,7 +8,8 @@ WORKDIR $WORKDIR
# deps install
COPY pyproject.toml __init__.py setup.sh $WORKDIR
RUN mkdir -p $WORKDIR/python/ && touch $WORKDIR/__init__.py
RUN mkdir -p $WORKDIR/python/ $WORKDIR/board/body/ $WORKDIR/board/jungle/ && \
touch $WORKDIR/__init__.py $WORKDIR/board/__init__.py $WORKDIR/board/body/__init__.py $WORKDIR/board/jungle/__init__.py
RUN apt-get update && apt-get install -y --no-install-recommends sudo && DEBIAN_FRONTEND=noninteractive $WORKDIR/setup.sh
# second pass for the opendbc moving tag

8
Jenkinsfile vendored
View File

@@ -15,7 +15,7 @@ def docker_run(String step_label, int timeout_mins, String cmd) {
def phone(String ip, String step_label, String cmd) {
withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) {
def ssh_cmd = """
ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
ssh -tt -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ConnectionAttempts=3 -o ServerAliveInterval=10 -o ServerAliveCountMax=3 -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END'
set -e
@@ -51,7 +51,11 @@ END"""
def phone_steps(String device_type, steps) {
lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) {
timeout(time: 20, unit: 'MINUTES') {
phone(device_ip, "git checkout", readFile("tests/setup_device_ci.sh"),)
retry (3) {
def date = sh(script: 'date', returnStdout: true).trim()
phone(device_ip, "set time", "date -s '${date}'")
phone(device_ip, "git checkout", readFile("tests/setup_device_ci.sh"))
}
steps.each { item ->
phone(device_ip, item[0], item[1])
}

View File

@@ -39,6 +39,8 @@ static void cuatro_set_fan_enabled(bool enabled) {
static void cuatro_set_bootkick(BootState state) {
set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK);
// DC_IN rising edge wakes SOM from ship mode
set_gpio_output(GPIOC, 11, state != BOOT_BOOTKICK);
}
static void cuatro_set_amp_enabled(bool enabled) {
@@ -50,7 +52,7 @@ static void cuatro_init(void) {
// open drain
set_gpio_output_type(GPIOD, 3, OUTPUT_TYPE_OPEN_DRAIN); // FAN_EN
set_gpio_output_type(GPIOC, 12, OUTPUT_TYPE_OPEN_DRAIN); // VBAT_EN
set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); // DC_IN_EN_N
// Power readout
set_gpio_mode(GPIOC, 5, MODE_ANALOG);

View File

@@ -6,7 +6,7 @@
#include "board/can.h"
#include "board/health.h"
#include "board/body/motor_control.h"
#include "board/drivers/can_common_declarations.h"
#include "board/drivers/drivers.h"
#include "opendbc/safety/declarations.h"
#define BODY_CAN_ADDR_MOTOR_SPEED 0x201U

View File

@@ -1,16 +1,15 @@
// ******************** Prototypes ********************
void print(const char *a){ UNUSED(a); }
void puth(uint8_t i){ UNUSED(i); }
void puth2(uint8_t i){ UNUSED(i); }
void puth4(uint8_t i){ UNUSED(i); }
void puth(unsigned int i){ UNUSED(i); }
void puth2(unsigned int i){ UNUSED(i); }
__attribute__((unused)) static void puth4(unsigned int i){ UNUSED(i); }
void hexdump(const void *a, int l){ UNUSED(a); UNUSED(l); }
typedef struct board board;
typedef struct harness_configuration harness_configuration;
void pwm_init(TIM_TypeDef *TIM, uint8_t channel);
void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage);
// No UART support in bootloader
typedef struct uart_ring {} uart_ring;
uart_ring uart_ring_som_debug;
typedef struct uart_ring uart_ring;
void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); }
// ********************* Globals **********************

View File

@@ -1,17 +0,0 @@
#pragma once
// ********************* Critical section helpers *********************
void enable_interrupts(void);
void disable_interrupts(void);
extern uint8_t global_critical_depth;
#define ENTER_CRITICAL() \
__disable_irq(); \
global_critical_depth += 1U;
#define EXIT_CRITICAL() \
global_critical_depth -= 1U; \
if ((global_critical_depth == 0U) && interrupts_enabled) { \
__enable_irq(); \
}

View File

@@ -1,4 +1,4 @@
#include "bootkick_declarations.h"
#include "board/drivers/drivers.h"
bool bootkick_reset_triggered = false;

View File

@@ -1,5 +0,0 @@
#pragma once
extern bool bootkick_reset_triggered;
void bootkick_tick(bool ignition, bool recent_heartbeat);

View File

@@ -1,4 +1,4 @@
#include "can_common_declarations.h"
#include "board/drivers/drivers.h"
uint32_t safety_tx_blocked = 0;
uint32_t safety_rx_invalid = 0;

View File

@@ -1,70 +0,0 @@
#pragma once
#include "board/can.h"
typedef struct {
volatile uint32_t w_ptr;
volatile uint32_t r_ptr;
uint32_t fifo_size;
CANPacket_t *elems;
} can_ring;
typedef struct {
uint8_t bus_lookup;
uint8_t can_num_lookup;
int8_t forwarding_bus;
uint32_t can_speed;
uint32_t can_data_speed;
bool canfd_auto;
bool canfd_enabled;
bool brs_enabled;
bool canfd_non_iso;
} bus_config_t;
extern uint32_t safety_tx_blocked;
extern uint32_t safety_rx_invalid;
extern uint32_t tx_buffer_overflow;
extern uint32_t rx_buffer_overflow;
extern can_health_t can_health[PANDA_CAN_CNT];
// Ignition detected from CAN meessages
extern bool ignition_can;
extern uint32_t ignition_can_cnt;
extern bool can_silent;
extern bool can_loopback;
// ******************* functions prototypes *********************
bool can_init(uint8_t can_number);
void process_can(uint8_t can_number);
// ********************* instantiate queues *********************
extern can_ring *can_queues[PANDA_CAN_CNT];
// helpers
#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU)
#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U))
// ********************* interrupt safe queue *********************
bool can_pop(can_ring *q, CANPacket_t *elem);
bool can_push(can_ring *q, const CANPacket_t *elem);
uint32_t can_slots_empty(const can_ring *q);
extern bus_config_t bus_config[PANDA_CAN_CNT];
#define CANIF_FROM_CAN_NUM(num) (cans[num])
#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup)
#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup)
void can_init_all(void);
void can_set_orientation(bool flipped);
#ifdef PANDA_JUNGLE
void can_set_forwarding(uint8_t from, uint8_t to);
#endif
void ignition_can_hook(CANPacket_t *to_push);
bool can_tx_check_min_slots_free(uint32_t min);
uint8_t calculate_checksum(const uint8_t *dat, uint32_t len);
void can_set_checksum(CANPacket_t *packet);
bool can_check_checksum(CANPacket_t *packet);
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len);

View File

@@ -1,4 +1,7 @@
#include "clock_source_declarations.h"
#include "board/drivers/drivers.h"
#define CLOCK_SOURCE_PERIOD_MS 50U
#define CLOCK_SOURCE_PULSE_LEN_MS 2U
void clock_source_set_timer_params(uint16_t param1, uint16_t param2) {
// Pulse length of each channel

View File

@@ -1,7 +0,0 @@
#pragma once
#define CLOCK_SOURCE_PERIOD_MS 50U
#define CLOCK_SOURCE_PULSE_LEN_MS 2U
void clock_source_set_timer_params(uint16_t param1, uint16_t param2);
void clock_source_init(bool enable_channel1);

287
board/drivers/drivers.h Normal file
View File

@@ -0,0 +1,287 @@
#pragma once
#include "board/can.h"
#include "board/health.h"
#include "board/crc.h"
#ifdef STM32H7
#include "board/stm32h7/lladc_declarations.h"
#endif
// ******************** bootkick ********************
extern bool bootkick_reset_triggered;
void bootkick_tick(bool ignition, bool recent_heartbeat);
// ******************** can_common ********************
typedef struct {
volatile uint32_t w_ptr;
volatile uint32_t r_ptr;
uint32_t fifo_size;
CANPacket_t *elems;
} can_ring;
typedef struct {
uint8_t bus_lookup;
uint8_t can_num_lookup;
int8_t forwarding_bus;
uint32_t can_speed;
uint32_t can_data_speed;
bool canfd_auto;
bool canfd_enabled;
bool brs_enabled;
bool canfd_non_iso;
} bus_config_t;
extern uint32_t safety_tx_blocked;
extern uint32_t safety_rx_invalid;
extern uint32_t tx_buffer_overflow;
extern uint32_t rx_buffer_overflow;
extern can_health_t can_health[PANDA_CAN_CNT];
// Ignition detected from CAN meessages
extern bool ignition_can;
extern uint32_t ignition_can_cnt;
extern bool can_silent;
extern bool can_loopback;
// ******************* functions prototypes *********************
bool can_init(uint8_t can_number);
void process_can(uint8_t can_number);
// ********************* instantiate queues *********************
extern can_ring *can_queues[PANDA_CAN_CNT];
// helpers
#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU)
#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U))
// ********************* interrupt safe queue *********************
bool can_pop(can_ring *q, CANPacket_t *elem);
bool can_push(can_ring *q, const CANPacket_t *elem);
uint32_t can_slots_empty(const can_ring *q);
extern bus_config_t bus_config[PANDA_CAN_CNT];
#define CANIF_FROM_CAN_NUM(num) (cans[num])
#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup)
#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup)
void can_init_all(void);
void can_set_orientation(bool flipped);
#ifdef PANDA_JUNGLE
void can_set_forwarding(uint8_t from, uint8_t to);
#endif
void ignition_can_hook(CANPacket_t *to_push);
bool can_tx_check_min_slots_free(uint32_t min);
uint8_t calculate_checksum(const uint8_t *dat, uint32_t len);
void can_set_checksum(CANPacket_t *packet);
bool can_check_checksum(CANPacket_t *packet);
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len);
// ******************** clock_source ********************
void clock_source_set_timer_params(uint16_t param1, uint16_t param2);
void clock_source_init(bool enable_channel1);
// ******************** fan ********************
struct fan_state_t {
uint16_t tach_counter;
uint16_t rpm;
uint8_t power;
float error_integral;
uint8_t cooldown_counter;
};
extern struct fan_state_t fan_state;
void fan_set_power(uint8_t percentage);
void llfan_init(void);
void fan_init(void);
// Call this at FAN_TICK_FREQ
void fan_tick(void);
// ******************** fdcan ********************
#ifdef STM32H7
typedef struct {
volatile uint32_t header[2];
volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U];
} canfd_fifo;
extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
#define CAN_ACK_ERROR 3U
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number);
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
void process_can(uint8_t can_number);
void can_rx(uint8_t can_number);
bool can_init(uint8_t can_number);
// ******************** harness ********************
#define HARNESS_STATUS_NC 0U
#define HARNESS_STATUS_NORMAL 1U
#define HARNESS_STATUS_FLIPPED 2U
struct harness_t {
uint8_t status;
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;
bool relay_driven;
bool sbu_adc_lock;
};
extern struct harness_t harness;
struct harness_configuration {
GPIO_TypeDef * const GPIO_SBU1;
GPIO_TypeDef * const GPIO_SBU2;
GPIO_TypeDef * const GPIO_relay_SBU1;
GPIO_TypeDef * const GPIO_relay_SBU2;
const uint8_t pin_SBU1;
const uint8_t pin_SBU2;
const uint8_t pin_relay_SBU1;
const uint8_t pin_relay_SBU2;
const adc_signal_t adc_signal_SBU1;
const adc_signal_t adc_signal_SBU2;
};
// The ignition relay is only used for testing purposes
void set_intercept_relay(bool intercept, bool ignition_relay);
bool harness_check_ignition(void);
void harness_tick(void);
void harness_init(void);
// ******************** interrupts ********************
typedef struct interrupt {
IRQn_Type irq_type;
void (*handler)(void);
uint32_t call_counter;
uint32_t call_rate;
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
uint32_t call_rate_fault;
} interrupt;
void interrupt_timer_init(void);
uint32_t microsecond_timer_get(void);
void unused_interrupt_handler(void);
extern interrupt interrupts[NUM_INTERRUPTS];
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \
interrupts[irq_num].irq_type = (irq_num); \
interrupts[irq_num].handler = (func_ptr); \
interrupts[irq_num].call_counter = 0U; \
interrupts[irq_num].call_rate = 0U; \
interrupts[irq_num].max_call_rate = (call_rate_max); \
interrupts[irq_num].call_rate_fault = (rate_fault);
extern float interrupt_load;
void handle_interrupt(IRQn_Type irq_type);
// Every second
void interrupt_timer_handler(void);
void init_interrupts(bool check_rate_limit);
#endif // STM32H7
// ******************** registers ********************
// 10 bit hash with 23 as a prime
#define REGISTER_MAP_SIZE 0x3FFU
#define HASHING_PRIME 23U
// Do not put bits in the check mask that get changed by the hardware
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask);
// Set individual bits. Also add them to the check_mask.
// Do not use this to change bits that get reset by the hardware
void register_set_bits(volatile uint32_t *addr, uint32_t val);
// Clear individual bits. Also add them to the check_mask.
// Do not use this to clear bits that get set by the hardware
void register_clear_bits(volatile uint32_t *addr, uint32_t val);
// To be called periodically
void check_registers(void);
void init_registers(void);
// ******************** simple_watchdog ********************
typedef struct simple_watchdog_state_t {
uint32_t fault;
uint32_t last_ts;
uint32_t threshold;
} simple_watchdog_state_t;
void simple_watchdog_kick(void);
void simple_watchdog_init(uint32_t fault, uint32_t threshold);
// ******************** spi ********************
// got max rate from hitting a non-existent endpoint
// in a tight loop, plus some buffer
#define SPI_IRQ_RATE 16000U
#define SPI_BUF_SIZE 4096U
extern uint8_t spi_buf_rx[SPI_BUF_SIZE];
extern uint8_t spi_buf_tx[SPI_BUF_SIZE];
extern uint16_t spi_error_count;
void can_tx_comms_resume_spi(void);
void spi_init(void);
void spi_rx_done(void);
void spi_tx_done(bool reset);
// ******************** uart ********************
#ifdef STM32H7
// ***************************** Definitions *****************************
#define FIFO_SIZE_INT 0x400U
typedef struct uart_ring {
volatile uint16_t w_ptr_tx;
volatile uint16_t r_ptr_tx;
uint8_t *elems_tx;
uint32_t tx_fifo_size;
volatile uint16_t w_ptr_rx;
volatile uint16_t r_ptr_rx;
uint8_t *elems_rx;
uint32_t rx_fifo_size;
USART_TypeDef *uart;
void (*callback)(struct uart_ring*);
bool overwrite;
} uart_ring;
// ***************************** Function prototypes *****************************
void debug_ring_callback(uart_ring *ring);
void uart_tx_ring(uart_ring *q);
uart_ring *get_ring_by_number(int a);
// ************************* Low-level buffer functions *************************
bool get_char(uart_ring *q, char *elem);
bool injectc(uart_ring *q, char elem);
bool put_char(uart_ring *q, char elem);
void clear_uart_buff(uart_ring *q);
// ************************ High-level debug functions **********************
void putch(const char a);
void print(const char *a);
void puthx(uint32_t i, uint8_t len);
void puth(unsigned int i);
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG)
static void puth4(unsigned int i);
#endif
#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
static void hexdump(const void *a, int l);
#endif
#endif // STM32H7
// ******************** usb ********************
void usb_init(void);
void refresh_can_tx_slots_available(void);
void can_tx_comms_resume_usb(void);

View File

@@ -1,4 +1,4 @@
#include "fan_declarations.h"
#include "board/drivers/drivers.h"
struct fan_state_t fan_state;

View File

@@ -1,16 +0,0 @@
#pragma once
struct fan_state_t {
uint16_t tach_counter;
uint16_t rpm;
uint8_t power;
float error_integral;
uint8_t cooldown_counter;
};
extern struct fan_state_t fan_state;
void fan_set_power(uint8_t percentage);
void llfan_init(void);
void fan_init(void);
// Call this at FAN_TICK_FREQ
void fan_tick(void);

View File

@@ -1,4 +1,4 @@
#include "fdcan_declarations.h"
#include "board/drivers/drivers.h"
FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3};

View File

@@ -1,19 +0,0 @@
#pragma once
#include "board/can.h"
typedef struct {
volatile uint32_t header[2];
volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U];
} canfd_fifo;
extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
#define CAN_ACK_ERROR 3U
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number);
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
void process_can(uint8_t can_number);
void can_rx(uint8_t can_number);
bool can_init(uint8_t can_number);

View File

@@ -1,4 +1,4 @@
#include "harness_declarations.h"
#include "board/drivers/drivers.h"
struct harness_t harness;

View File

@@ -1,33 +0,0 @@
#pragma once
#define HARNESS_STATUS_NC 0U
#define HARNESS_STATUS_NORMAL 1U
#define HARNESS_STATUS_FLIPPED 2U
struct harness_t {
uint8_t status;
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;
bool relay_driven;
bool sbu_adc_lock;
};
extern struct harness_t harness;
struct harness_configuration {
GPIO_TypeDef * const GPIO_SBU1;
GPIO_TypeDef * const GPIO_SBU2;
GPIO_TypeDef * const GPIO_relay_SBU1;
GPIO_TypeDef * const GPIO_relay_SBU2;
const uint8_t pin_SBU1;
const uint8_t pin_SBU2;
const uint8_t pin_relay_SBU1;
const uint8_t pin_relay_SBU2;
const adc_signal_t adc_signal_SBU1;
const adc_signal_t adc_signal_SBU2;
};
// The ignition relay is only used for testing purposes
void set_intercept_relay(bool intercept, bool ignition_relay);
bool harness_check_ignition(void);
void harness_tick(void);
void harness_init(void);

View File

@@ -1,4 +1,4 @@
#include "interrupts_declarations.h"
#include "board/drivers/drivers.h"
void unused_interrupt_handler(void) {
// Something is wrong if this handler is called!

View File

@@ -1,31 +0,0 @@
#pragma once
typedef struct interrupt {
IRQn_Type irq_type;
void (*handler)(void);
uint32_t call_counter;
uint32_t call_rate;
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
uint32_t call_rate_fault;
} interrupt;
void interrupt_timer_init(void);
uint32_t microsecond_timer_get(void);
void unused_interrupt_handler(void);
extern interrupt interrupts[NUM_INTERRUPTS];
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \
interrupts[irq_num].irq_type = (irq_num); \
interrupts[irq_num].handler = (func_ptr); \
interrupts[irq_num].call_counter = 0U; \
interrupts[irq_num].call_rate = 0U; \
interrupts[irq_num].max_call_rate = (call_rate_max); \
interrupts[irq_num].call_rate_fault = (rate_fault);
extern float interrupt_load;
void handle_interrupt(IRQn_Type irq_type);
// Every second
void interrupt_timer_handler(void);
void init_interrupts(bool check_rate_limit);

View File

@@ -1,4 +1,13 @@
#include "registers_declarations.h"
#include "board/drivers/drivers.h"
typedef struct reg {
volatile uint32_t *address;
uint32_t value;
uint32_t check_mask;
bool logged_fault;
} reg;
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr)))
static reg register_map[REGISTER_MAP_SIZE];

View File

@@ -1,24 +0,0 @@
#pragma once
typedef struct reg {
volatile uint32_t *address;
uint32_t value;
uint32_t check_mask;
} reg;
// 10 bit hash with 23 as a prime
#define REGISTER_MAP_SIZE 0x3FFU
#define HASHING_PRIME 23U
#define CHECK_COLLISION(hash, addr) (((uint32_t) register_map[hash].address != 0U) && (register_map[hash].address != (addr)))
// Do not put bits in the check mask that get changed by the hardware
void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask);
// Set individual bits. Also add them to the check_mask.
// Do not use this to change bits that get reset by the hardware
void register_set_bits(volatile uint32_t *addr, uint32_t val);
// Clear individual bits. Also add them to the check_mask.
// Do not use this to clear bits that get set by the hardware
void register_clear_bits(volatile uint32_t *addr, uint32_t val);
// To be called periodically
void check_registers(void);
void init_registers(void);

View File

@@ -1,4 +1,4 @@
#include "simple_watchdog_declarations.h"
#include "board/drivers/drivers.h"
static simple_watchdog_state_t wd_state;

View File

@@ -1,10 +0,0 @@
#pragma once
typedef struct simple_watchdog_state_t {
uint32_t fault;
uint32_t last_ts;
uint32_t threshold;
} simple_watchdog_state_t;
void simple_watchdog_kick(void);
void simple_watchdog_init(uint32_t fault, uint32_t threshold);

View File

@@ -1,13 +1,41 @@
#pragma once
#include "board/drivers/spi_declarations.h"
#include "board/crc.h"
#include "board/drivers/drivers.h"
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2
#ifdef STM32H7
__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE];
__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE];
#else
uint8_t spi_buf_rx[SPI_BUF_SIZE];
uint8_t spi_buf_tx[SPI_BUF_SIZE];
#endif
#define SPI_CHECKSUM_START 0xABU
#define SPI_SYNC_BYTE 0x5AU
#define SPI_HACK 0x79U
#define SPI_DACK 0x85U
#define SPI_NACK 0x1FU
// SPI states
enum {
SPI_STATE_HEADER,
SPI_STATE_HEADER_ACK,
SPI_STATE_HEADER_NACK,
SPI_STATE_DATA_RX,
SPI_STATE_DATA_RX_ACK,
SPI_STATE_DATA_TX
};
uint16_t spi_error_count = 0;
#define SPI_HEADER_SIZE 7U
// low level SPI prototypes
void llspi_init(void);
void llspi_mosi_dma(uint8_t *addr, int len);
void llspi_miso_dma(uint8_t *addr, int len);
static uint8_t spi_state = SPI_STATE_HEADER;
static uint16_t spi_data_len_mosi;
static bool spi_can_tx_ready = false;

View File

@@ -1,44 +0,0 @@
#pragma once
#include "board/crc.h"
#define SPI_TIMEOUT_US 10000U
// got max rate from hitting a non-existent endpoint
// in a tight loop, plus some buffer
#define SPI_IRQ_RATE 16000U
#define SPI_BUF_SIZE 4096U
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2
__attribute__((section(".sram12"))) extern uint8_t spi_buf_rx[SPI_BUF_SIZE];
__attribute__((section(".sram12"))) extern uint8_t spi_buf_tx[SPI_BUF_SIZE];
#define SPI_CHECKSUM_START 0xABU
#define SPI_SYNC_BYTE 0x5AU
#define SPI_HACK 0x79U
#define SPI_DACK 0x85U
#define SPI_NACK 0x1FU
// SPI states
enum {
SPI_STATE_HEADER,
SPI_STATE_HEADER_ACK,
SPI_STATE_HEADER_NACK,
SPI_STATE_DATA_RX,
SPI_STATE_DATA_RX_ACK,
SPI_STATE_DATA_TX
};
extern uint16_t spi_error_count;
#define SPI_HEADER_SIZE 7U
// low level SPI prototypes
void llspi_init(void);
void llspi_mosi_dma(uint8_t *addr, int len);
void llspi_miso_dma(uint8_t *addr, int len);
void can_tx_comms_resume_spi(void);
void spi_init(void);
void spi_rx_done(void);
void spi_tx_done(bool reset);

View File

@@ -1,4 +1,4 @@
#include "uart_declarations.h"
#include "board/drivers/drivers.h"
// ***************************** Definitions *****************************

View File

@@ -1,43 +0,0 @@
#pragma once
// ***************************** Definitions *****************************
#ifdef STM32H7
#define FIFO_SIZE_INT 0x400U
#else
#define FIFO_SIZE_INT 0x200U
#endif
typedef struct uart_ring {
volatile uint16_t w_ptr_tx;
volatile uint16_t r_ptr_tx;
uint8_t *elems_tx;
uint32_t tx_fifo_size;
volatile uint16_t w_ptr_rx;
volatile uint16_t r_ptr_rx;
uint8_t *elems_rx;
uint32_t rx_fifo_size;
USART_TypeDef *uart;
void (*callback)(struct uart_ring*);
bool overwrite;
} uart_ring;
// ***************************** Function prototypes *****************************
void debug_ring_callback(uart_ring *ring);
void uart_tx_ring(uart_ring *q);
uart_ring *get_ring_by_number(int a);
// ************************* Low-level buffer functions *************************
bool get_char(uart_ring *q, char *elem);
bool injectc(uart_ring *q, char elem);
bool put_char(uart_ring *q, char elem);
void clear_uart_buff(uart_ring *q);
// ************************ High-level debug functions **********************
void putch(const char a);
void print(const char *a);
void puthx(uint32_t i, uint8_t len);
void puth(unsigned int i);
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG)
static void puth4(unsigned int i);
#endif
#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
static void hexdump(const void *a, int l);
#endif

View File

@@ -1,4 +1,87 @@
#include "usb_declarations.h"
#include "board/drivers/drivers.h"
// IRQs: OTG_FS
typedef union {
uint16_t w;
struct BW {
uint8_t msb;
uint8_t lsb;
}
bw;
} uint16_t_uint8_t;
typedef union _USB_Setup {
uint32_t d8[2];
struct _SetupPkt_Struc
{
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t_uint8_t wValue;
uint16_t_uint8_t wIndex;
uint16_t_uint8_t wLength;
} b;
} USB_Setup_TypeDef;
// **** supporting defines ****
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_DESC_TYPE_DEVICE 0x01
#define USB_DESC_TYPE_CONFIGURATION 0x02
#define USB_DESC_TYPE_STRING 0x03
#define USB_DESC_TYPE_INTERFACE 0x04
#define USB_DESC_TYPE_ENDPOINT 0x05
#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06
#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f
// offsets for configuration strings
#define STRING_OFFSET_LANGID 0x00
#define STRING_OFFSET_IMANUFACTURER 0x01
#define STRING_OFFSET_IPRODUCT 0x02
#define STRING_OFFSET_ISERIAL 0x03
#define STRING_OFFSET_ICONFIGURATION 0x04
// WinUSB requests
#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04
#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05
#define WINUSB_REQ_GET_DESCRIPTOR 0x07
#define STS_DATA_UPDT 2
#define STS_SETUP_UPDT 6
// for the repeating interfaces
#define DSCR_INTERFACE_LEN 9
#define DSCR_ENDPOINT_LEN 7
#define DSCR_CONFIG_LEN 9
#define DSCR_DEVICE_LEN 18
// endpoint types
#define ENDPOINT_TYPE_BULK 2
#define ENDPOINT_TYPE_INT 3
// These are arbitrary values used in bRequest
#define MS_VENDOR_CODE 0x20
#define WEBUSB_VENDOR_CODE 0x30
// BOS constants
#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05
#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F
#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E
// Convert machine byte order to USB byte order
#define TOUSBORDER(num)\
((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU)
// take in string length and return the first 2 bytes of a string descriptor
#define STRING_DESCRIPTOR_HEADER(size)\
(((((size) * 2) + 2) & 0xFF) | 0x0300)
#define ENDPOINT_RCV 0x80
#define ENDPOINT_SND 0x00
static uint8_t response[USBPACKET_MAX_SIZE];

View File

@@ -1,111 +0,0 @@
#pragma once
// IRQs: OTG_FS
typedef union {
uint16_t w;
struct BW {
uint8_t msb;
uint8_t lsb;
}
bw;
} uint16_t_uint8_t;
typedef union _USB_Setup {
uint32_t d8[2];
struct _SetupPkt_Struc
{
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t_uint8_t wValue;
uint16_t_uint8_t wIndex;
uint16_t_uint8_t wLength;
} b;
} USB_Setup_TypeDef;
void usb_init(void);
void refresh_can_tx_slots_available(void);
// **** supporting defines ****
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USB_DESC_TYPE_DEVICE 0x01
#define USB_DESC_TYPE_CONFIGURATION 0x02
#define USB_DESC_TYPE_STRING 0x03
#define USB_DESC_TYPE_INTERFACE 0x04
#define USB_DESC_TYPE_ENDPOINT 0x05
#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07
#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f
// offsets for configuration strings
#define STRING_OFFSET_LANGID 0x00
#define STRING_OFFSET_IMANUFACTURER 0x01
#define STRING_OFFSET_IPRODUCT 0x02
#define STRING_OFFSET_ISERIAL 0x03
#define STRING_OFFSET_ICONFIGURATION 0x04
#define STRING_OFFSET_IINTERFACE 0x05
// WebUSB requests
#define WEBUSB_REQ_GET_URL 0x02
// WebUSB types
#define WEBUSB_DESC_TYPE_URL 0x03
#define WEBUSB_URL_SCHEME_HTTPS 0x01
#define WEBUSB_URL_SCHEME_HTTP 0x00
// WinUSB requests
#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04
#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05
#define WINUSB_REQ_GET_DESCRIPTOR 0x07
#define STS_GOUT_NAK 1
#define STS_DATA_UPDT 2
#define STS_XFER_COMP 3
#define STS_SETUP_COMP 4
#define STS_SETUP_UPDT 6
// for the repeating interfaces
#define DSCR_INTERFACE_LEN 9
#define DSCR_ENDPOINT_LEN 7
#define DSCR_CONFIG_LEN 9
#define DSCR_DEVICE_LEN 18
// endpoint types
#define ENDPOINT_TYPE_CONTROL 0
#define ENDPOINT_TYPE_ISO 1
#define ENDPOINT_TYPE_BULK 2
#define ENDPOINT_TYPE_INT 3
// These are arbitrary values used in bRequest
#define MS_VENDOR_CODE 0x20
#define WEBUSB_VENDOR_CODE 0x30
// BOS constants
#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05
#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F
#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E
// Convert machine byte order to USB byte order
#define TOUSBORDER(num)\
((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU)
// take in string length and return the first 2 bytes of a string descriptor
#define STRING_DESCRIPTOR_HEADER(size)\
(((((size) * 2) + 2) & 0xFF) | 0x0300)
#define ENDPOINT_RCV 0x80
#define ENDPOINT_SND 0x00
// ***************************** USB port *****************************
void can_tx_comms_resume_usb(void);

View File

@@ -5,8 +5,6 @@
uint32_t *prog_ptr = NULL;
bool unlocked = false;
void spi_init(void);
int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
int resp_len = 0;

View File

@@ -1,6 +1,8 @@
#pragma once
// When changing these structs, python/__init__.py needs to be kept up to date!
#define HEALTH_PACKET_VERSION 17
#define HEALTH_PACKET_VERSION 18
struct __attribute__((packed)) health_t {
uint32_t uptime_pkt;
uint32_t voltage_pkt;
@@ -27,6 +29,7 @@ struct __attribute__((packed)) health_t {
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;
uint8_t som_reset_triggered;
uint16_t sound_output_level_pkt;
};
#define CAN_HEALTH_PACKET_VERSION 5

View File

@@ -52,7 +52,7 @@ class PandaJungle(Panda):
def flash(self, fn=None, code=None, reconnect=True):
if not fn:
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn.replace("panda", "panda_jungle"))
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn.replace("panda", "panda_jungle"))
super().flash(fn=fn, code=code, reconnect=reconnect)
def recover(self, timeout: int | None = 60, reset: bool = True) -> bool:
@@ -73,15 +73,9 @@ class PandaJungle(Panda):
self.flash()
return True
def get_mcu_type(self) -> McuType:
hw_type = self.get_type()
if hw_type in PandaJungle.H7_DEVICES:
return McuType.H7
raise ValueError(f"unknown HW type: {hw_type}")
def up_to_date(self, fn=None) -> bool:
if fn is None:
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn.replace("panda", "panda_jungle"))
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn.replace("panda", "panda_jungle"))
return super().up_to_date(fn=fn)
# ******************* health *******************

View File

@@ -18,7 +18,7 @@
#include "board/drivers/fdcan.h"
#include "board/power_saving.h"
#include "board/sys/power_saving.h"
#include "board/obj/gitversion.h"
@@ -148,7 +148,7 @@ static void tick_handler(void) {
// re-init everything that uses harness status
can_init_all();
set_safety_mode(current_safety_mode, current_safety_param);
set_power_save_state(power_save_status);
set_power_save_state(power_save_enabled);
}
// decimated to 1Hz
@@ -167,7 +167,7 @@ static void tick_handler(void) {
// turn off the blue LED, turned on by CAN
// unless we are in power saving mode
led_set(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED));
led_set(LED_BLUE, (uptime_cnt & 1U) && power_save_enabled);
const bool recent_heartbeat = heartbeat_counter == 0U;
@@ -233,8 +233,8 @@ static void tick_handler(void) {
set_safety_mode(SAFETY_SILENT, 0U);
}
if (power_save_status != POWER_SAVE_STATUS_ENABLED) {
set_power_save_state(POWER_SAVE_STATUS_ENABLED);
if (!power_save_enabled) {
set_power_save_state(true);
}
// Also disable IR when the heartbeat goes missing
@@ -343,7 +343,12 @@ int main(void) {
// LED should keep on blinking all the time
while (true) {
if (power_save_status == POWER_SAVE_STATUS_DISABLED) {
#ifdef ALLOW_DEBUG
if (stop_mode_requested) {
enter_stop_mode();
}
#endif
if (!power_save_enabled) {
#ifdef DEBUG_FAULTS
if (fault_status == FAULT_STATUS_NONE) {
#endif
@@ -371,6 +376,11 @@ int main(void) {
}
#endif
} else {
if ((hw_type == HW_TYPE_CUATRO) && !current_board->read_som_gpio()) {
assert_fatal(current_safety_mode == SAFETY_SILENT, "Error: Entering low power mode while not in SAFETY_SILENT. Hanging\n");
enter_stop_mode(); // deep sleep, wakes on CAN or SBU activity
assert_fatal(false, "Error: enter_stop_mode returned after system reset. Hanging\n");
}
__WFI();
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
}

View File

@@ -24,7 +24,7 @@ static int get_health_pkt(void *dat) {
health->safety_mode_pkt = (uint8_t)(current_safety_mode);
health->safety_param_pkt = current_safety_param;
health->alternative_experience_pkt = alternative_experience;
health->power_save_enabled_pkt = power_save_status == POWER_SAVE_STATUS_ENABLED;
health->power_save_enabled_pkt = power_save_enabled;
health->heartbeat_lost_pkt = heartbeat_lost;
health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid;
@@ -42,6 +42,8 @@ static int get_health_pkt(void *dat) {
health->som_reset_triggered = bootkick_reset_triggered;
health->sound_output_level_pkt = sound_output_level;
return sizeof(*health);
}
@@ -95,6 +97,14 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U);
resp_len = 2;
break;
// **** 0xb5: request deep sleep, wakes on CAN or SBU
#ifdef ALLOW_DEBUG
case 0xb5:
set_safety_mode(SAFETY_SILENT, 0U);
set_power_save_state(true);
stop_mode_requested = true;
break;
#endif
// **** 0xc0: reset communications state
case 0xc0:
comms_can_reset();
@@ -265,7 +275,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break;
// **** 0xe7: set power save state
case 0xe7:
set_power_save_state(req->param1);
set_power_save_state(req->param1 != 0U);
break;
// **** 0xe8: set can-fd auto swithing mode
case 0xe8:

View File

@@ -20,3 +20,6 @@ extern bool heartbeat_disabled;
// siren state
extern bool siren_enabled;
// sound
extern uint16_t sound_output_level;

View File

@@ -1,52 +0,0 @@
#include "power_saving_declarations.h"
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
// See rule: CoU_3
int power_save_status = POWER_SAVE_STATUS_DISABLED;
void enable_can_transceivers(bool enabled) {
// Leave main CAN always on for CAN-based ignition detection
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
for(uint8_t i=1U; i<=4U; i++){
current_board->enable_can_transceiver(i, (i == main_bus) || enabled);
}
}
void set_power_save_state(int state) {
bool is_valid_state = (state == POWER_SAVE_STATUS_ENABLED) || (state == POWER_SAVE_STATUS_DISABLED);
if (is_valid_state && (state != power_save_status)) {
bool enable = false;
if (state == POWER_SAVE_STATUS_ENABLED) {
print("enable power savings\n");
// Disable CAN interrupts
if (harness.status == HARNESS_STATUS_FLIPPED) {
llcan_irq_disable(cans[0]);
} else {
llcan_irq_disable(cans[2]);
}
llcan_irq_disable(cans[1]);
} else {
print("disable power savings\n");
if (harness.status == HARNESS_STATUS_FLIPPED) {
llcan_irq_enable(cans[0]);
} else {
llcan_irq_enable(cans[2]);
}
llcan_irq_enable(cans[1]);
enable = true;
}
enable_can_transceivers(enable);
// Switch off IR when in power saving
if(!enable){
current_board->set_ir_power(0U);
}
power_save_status = state;
}
}

View File

@@ -1,11 +0,0 @@
#pragma once
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes.
// See rule: CoU_3
#define POWER_SAVE_STATUS_DISABLED 0
#define POWER_SAVE_STATUS_ENABLED 1
extern int power_save_status;
void set_power_save_state(int state);

View File

@@ -12,6 +12,7 @@ __attribute__((section(".sram4"))) static uint16_t mic_tx_buf[2][MIC_TX_BUF_SIZE
static uint8_t sound_idle_count;
static uint8_t mic_idle_count;
static uint8_t mic_buffer_count;
uint16_t sound_output_level;
void sound_tick(void) {
if (sound_idle_count > 0U) {
@@ -19,6 +20,7 @@ void sound_tick(void) {
if (sound_idle_count == 0U) {
current_board->set_amp_enabled(false);
register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN);
sound_output_level = 0U;
}
}
@@ -72,14 +74,31 @@ static void BDMA_Channel0_IRQ_Handler(void) {
// process samples (shift to 12b and bias to be unsigned)
bool sound_playing = false;
uint32_t abs_sum = 0U;
for (uint16_t i=0U; i < SOUND_RX_BUF_SIZE; i += 2U) {
// since we are playing mono and receiving stereo, we take every other sample
sound_tx_buf[playback_buf][i/2U] = ((sound_rx_buf[rx_buf_idx][i] + (1UL << 14)) >> 3);
uint16_t sample = ((sound_rx_buf[rx_buf_idx][i] + (1UL << 14)) >> 3) & 0xFFFU;
sound_tx_buf[playback_buf][i/2U] = sample;
if (sound_rx_buf[rx_buf_idx][i] > 0U) {
sound_playing = true;
}
// this assumes all audio is "zero" centered
if (sample > 0x7FFU) {
abs_sum += (uint32_t)sample - 0x7FFU;
} else {
abs_sum += 0x7FFU - (uint32_t)sample;
}
}
// VU meter: fast attack, slow decay (~460ms half-life at ~96Hz ISR rate)
uint16_t level = (uint16_t)(abs_sum / (SOUND_RX_BUF_SIZE / 2U));
if (level >= sound_output_level) {
sound_output_level = level;
}
sound_output_level -= (sound_output_level >> 6U);
// manage amp state
if (sound_playing) {
if (sound_idle_count == 0U) {
@@ -102,8 +121,6 @@ static void BDMA_Channel0_IRQ_Handler(void) {
DFSDM1_Filter0->FLTCR1 |= DFSDM_FLTCR1_RSWSTART;
}
mic_idle_count = SOUND_IDLE_TIMEOUT;
sound_tick();
}
void sound_init_dac(void) {

View File

@@ -53,12 +53,16 @@ separate IRQs for RX and TX.
#endif
#include "board/libc.h"
#include "board/critical.h"
#include "board/faults.h"
#include "board/sys/critical.h"
#include "board/sys/faults.h"
#include "board/utils.h"
#include "board/drivers/registers.h"
#include "board/drivers/interrupts.h"
#ifdef BOOTSTUB
uart_ring uart_ring_som_debug;
#endif
#include "board/drivers/gpio.h"
#include "board/stm32h7/peripherals.h"
#include "board/stm32h7/interrupt_handlers.h"

View File

@@ -1,4 +1,4 @@
#include "critical_declarations.h"
#include "board/sys/sys.h"
// ********************* Critical section helpers *********************
uint8_t global_critical_depth = 0U;

View File

@@ -1,4 +1,4 @@
#include "faults_declarations.h"
#include "board/sys/sys.h"
uint8_t fault_status = FAULT_STATUS_NONE;
uint32_t faults = 0U;

148
board/sys/power_saving.h Normal file
View File

@@ -0,0 +1,148 @@
#include "board/sys/sys.h"
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM2331, we should never use any of the available hardware low power modes during safety function execution.
// See rule: CoU_3
// Low power state "stop mode" is only entered from SAFETY_SILENT when no safety function is active and exited via reset which is a safe state.
bool power_save_enabled = false;
#ifdef ALLOW_DEBUG
volatile bool stop_mode_requested = false;
#endif
void enable_can_transceivers(bool enabled) {
// Leave main CAN always on for CAN-based ignition detection
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
for(uint8_t i=1U; i<=4U; i++){
current_board->enable_can_transceiver(i, (i == main_bus) || enabled);
}
}
void set_power_save_state(bool enable) {
if (enable != power_save_enabled) {
if (enable) {
print("enable power savings\n");
// Disable CAN interrupts
if (harness.status == HARNESS_STATUS_FLIPPED) {
llcan_irq_disable(cans[0]);
} else {
llcan_irq_disable(cans[2]);
}
llcan_irq_disable(cans[1]);
} else {
print("disable power savings\n");
if (harness.status == HARNESS_STATUS_FLIPPED) {
llcan_irq_enable(cans[0]);
} else {
llcan_irq_enable(cans[2]);
}
llcan_irq_enable(cans[1]);
}
enable_can_transceivers(!enable);
// Switch off IR when in power saving
if(enable){
current_board->set_ir_power(0U);
}
power_save_enabled = enable;
}
}
static void enter_stop_mode(void) {
// set all GPIO to analog mode to reduce power, analog mode also disables pull resistors
register_set(&(GPIOA->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOB->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOC->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOD->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOE->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOF->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
register_set(&(GPIOG->MODER), 0xFFFFFFFFU, 0xFFFFFFFFU);
// init GPIO to lowest power state
current_board->set_bootkick(BOOT_STANDBY);
current_board->set_amp_enabled(false);
for (uint8_t i = 1U; i <= 4U; i++) {
current_board->enable_can_transceiver(i, false);
}
// disable ADCs
ADC1->CR &= ~(ADC_CR_ADEN);
ADC1->CR |= ADC_CR_DEEPPWD;
ADC2->CR &= ~(ADC_CR_ADEN);
ADC2->CR |= ADC_CR_DEEPPWD;
// disable HSI48: 48 MHz USB clock
register_clear_bits(&(RCC->CR), RCC_CR_HSI48ON);
// disable SRAM retention in stop mode
register_clear_bits(&(RCC->AHB2LPENR), RCC_AHB2LPENR_SRAM1LPEN | RCC_AHB2LPENR_SRAM2LPEN);
register_clear_bits(&(RCC->AHB4LPENR), RCC_AHB4LPENR_SRAM4LPEN);
register_clear_bits(&(RCC->AHB3LPENR), RCC_AHB3LPENR_AXISRAMLPEN);
// SBU pins to input for EXTI wakeup
set_gpio_mode(current_board->harness_config->GPIO_SBU1,
current_board->harness_config->pin_SBU1, MODE_INPUT);
set_gpio_mode(current_board->harness_config->GPIO_SBU2,
current_board->harness_config->pin_SBU2, MODE_INPUT);
// EXTI1: SBU2 (PA1)
// EXTI4: SBU1 (PC4)
register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI1_PA, 0xF0U);
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI4_PC, 0xFU);
register_set_bits(&(EXTI->IMR1), (1U << 1) | (1U << 4));
register_set_bits(&(EXTI->RTSR1), (1U << 1) | (1U << 4));
register_set_bits(&(EXTI->FTSR1), (1U << 1) | (1U << 4));
// EXTI for CAN wakeup
// EXTI8: FDCAN1 RX (PB8)
// EXTI5: FDCAN2 RX (PB5)
// EXTI12: FDCAN3 RX (PD12)
set_gpio_mode(GPIOB, 8, MODE_INPUT);
register_set(&(SYSCFG->EXTICR[2]), SYSCFG_EXTICR3_EXTI8_PB, 0xFU);
set_gpio_mode(GPIOB, 5, MODE_INPUT);
register_set(&(SYSCFG->EXTICR[1]), SYSCFG_EXTICR2_EXTI5_PB, 0xF0U);
set_gpio_mode(GPIOD, 12, MODE_INPUT);
register_set(&(SYSCFG->EXTICR[3]), SYSCFG_EXTICR4_EXTI12_PD, 0xFU);
uint32_t can_exti_line = (1UL << 8) | (1UL << 5) | (1UL << 12);
register_set_bits(&(EXTI->IMR1), can_exti_line);
register_set_bits(&(EXTI->FTSR1), can_exti_line);
// clear pending EXTI
EXTI->PR1 = (1U << 1) | (1U << 4) | can_exti_line;
// reset if ignition just came on before going to sleep
if (harness_check_ignition()) {
NVIC_SystemReset();
}
// stop mode
register_clear_bits(&(PWR->CPUCR), PWR_CPUCR_PDDS_D1 | PWR_CPUCR_PDDS_D2 | PWR_CPUCR_PDDS_D3);
// set SVOS5 voltage scaling, flash low-power
register_set(&(PWR->CR1), PWR_CR1_SVOS_0 | PWR_CR1_FLPS, PWR_CR1_SVOS | PWR_CR1_FLPS);
// enter stop mode on WFI
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__disable_irq();
// disable all NVIC interrupts and clear pending
for (uint32_t i = 0U; i < 8U; i++) {
NVIC->ICER[i] = 0xFFFFFFFFU;
NVIC->ICPR[i] = 0xFFFFFFFFU;
}
// enable only wakeup EXTI interrupts
NVIC_EnableIRQ(EXTI1_IRQn); // SBU2 (PA1)
NVIC_EnableIRQ(EXTI4_IRQn); // SBU1 (PC4)
NVIC_EnableIRQ(EXTI9_5_IRQn); // FDCAN1 RX (PB8), FDCAN2 RX (PB5)
NVIC_EnableIRQ(EXTI15_10_IRQn); // FDCAN3 RX (PD12)
__DSB();
__ISB();
__WFI();
NVIC_SystemReset();
}

View File

@@ -1,5 +1,28 @@
#pragma once
// ******************** critical ********************
void enable_interrupts(void);
void disable_interrupts(void);
extern uint8_t global_critical_depth;
#ifndef ENTER_CRITICAL
#define ENTER_CRITICAL() \
__disable_irq(); \
global_critical_depth += 1U;
#endif
#ifndef EXIT_CRITICAL
#define EXIT_CRITICAL() \
global_critical_depth -= 1U; \
if ((global_critical_depth == 0U) && interrupts_enabled) { \
__enable_irq(); \
}
#endif
// ******************** faults ********************
#define FAULT_STATUS_NONE 0U
#define FAULT_STATUS_TEMPORARY 1U
#define FAULT_STATUS_PERMANENT 2U
@@ -32,3 +55,9 @@ extern uint32_t faults;
void fault_occurred(uint32_t fault);
void fault_recovered(uint32_t fault);
// ******************** power_saving ********************
extern bool power_save_enabled;
void set_power_save_state(bool enable);

View File

@@ -30,6 +30,8 @@ dev = [
"mypy",
"setuptools",
"spidev; platform_system == 'Linux'",
"gcc-arm-none-eabi @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=gcc-arm-none-eabi",
"cppcheck @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=cppcheck",
]
[build-system]
@@ -37,7 +39,13 @@ requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["panda"]
packages = [
"panda",
"panda.python",
"panda.board",
"panda.board.body",
"panda.board.jungle",
]
[tool.setuptools.package-dir]
panda = "."

View File

@@ -111,20 +111,17 @@ class Panda:
# from https://github.com/commaai/openpilot/blob/103b4df18cbc38f4129555ab8b15824d1a672bdf/cereal/log.capnp#L648
HW_TYPE_UNKNOWN = b'\x00'
HW_TYPE_WHITE = b'\x01'
HW_TYPE_BLACK = b'\x03'
HW_TYPE_RED_PANDA = b'\x07'
HW_TYPE_TRES = b'\x09'
HW_TYPE_CUATRO = b'\x0a'
HW_TYPE_BODY = b'\xb1'
CAN_PACKET_VERSION = 4
HEALTH_PACKET_VERSION = 17
HEALTH_PACKET_VERSION = 18
CAN_HEALTH_PACKET_VERSION = 5
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHHHB")
HEALTH_STRUCT = struct.Struct("<IIIIIIIIBBBBBHBBBHfBBHHHBH")
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBBIIII")
F4_DEVICES = [HW_TYPE_WHITE, HW_TYPE_BLACK]
H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_TRES, HW_TYPE_CUATRO, HW_TYPE_BODY]
SUPPORTED_DEVICES = H7_DEVICES
@@ -147,7 +144,6 @@ class Panda:
else:
self._connect_serial = serial
# connect and set mcu type
self.connect(claim)
def _cli_select_panda(self):
@@ -204,14 +200,9 @@ class Panda:
self._serial = serial
self._connect_serial = serial
self._handle_open = True
self._mcu_type = self.get_mcu_type()
self.health_version, self.can_version, self.can_health_version = self.get_packets_versions()
logger.debug("connected")
hw_type = self.get_type()
if hw_type not in self.SUPPORTED_DEVICES:
print("WARNING: Using deprecated HW")
# disable openpilot's heartbeat checks
if self._disable_checks:
self.set_heartbeat_disabled()
@@ -338,6 +329,9 @@ class Panda:
return []
def reset(self, enter_bootstub=False, enter_bootloader=False, reconnect=True):
if enter_bootstub or enter_bootloader:
assert (hw_type := self.get_type()) in self.SUPPORTED_DEVICES, f"Unknown HW: {hw_type}"
# no response is expected since it resets right away
timeout = 5000 if isinstance(self._handle, PandaSpiHandle) else 15000
try:
@@ -417,16 +411,14 @@ class Panda:
pass
def flash(self, fn=None, code=None, reconnect=True):
assert (hw_type := self.get_type()) in self.SUPPORTED_DEVICES, f"Unknown HW: {hw_type}"
if self.up_to_date(fn=fn):
logger.info("flash: already up to date")
return
hw_type = self.get_type()
if hw_type not in self.SUPPORTED_DEVICES:
raise RuntimeError(f"HW type {hw_type.hex()} is deprecated and can no longer be flashed.")
if not fn:
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn)
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
assert os.path.isfile(fn)
logger.debug("flash: main version is %s", self.get_version())
if not self.bootstub:
@@ -441,7 +433,7 @@ class Panda:
logger.debug("flash: bootstub version is %s", self.get_version())
# do flash
Panda.flash_static(self._handle, code, mcu_type=self._mcu_type)
Panda.flash_static(self._handle, code, mcu_type=McuType.H7)
# reconnect
if reconnect:
@@ -492,7 +484,7 @@ class Panda:
def up_to_date(self, fn=None) -> bool:
current = self.get_signature()
if fn is None:
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
expected = Panda.get_signature_from_firmware(fn)
return (current == expected)
@@ -531,6 +523,7 @@ class Panda:
"sbu1_voltage_mV": a[22],
"sbu2_voltage_mV": a[23],
"som_reset_triggered": a[24],
"sound_output_level": a[25],
}
@ensure_can_health_packet_version
@@ -604,14 +597,6 @@ class Panda:
else:
return (0, 0, 0)
def get_mcu_type(self) -> McuType:
hw_type = self.get_type()
if hw_type in Panda.F4_DEVICES:
return McuType.F4
elif hw_type in Panda.H7_DEVICES:
return McuType.H7
raise ValueError(f"unknown HW type: {hw_type}")
def is_internal(self):
return self.get_type() in Panda.INTERNAL_DEVICES
@@ -632,7 +617,7 @@ class Panda:
return self._serial
def get_dfu_serial(self):
return PandaDFU.st_serial_to_dfu_serial(self._serial, self._mcu_type)
return PandaDFU.st_serial_to_dfu_serial(self._serial, McuType.H7)
def get_uid(self):
"""
@@ -656,6 +641,9 @@ class Panda:
def set_power_save(self, power_save_enabled=0):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
def enter_stop_mode(self):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb5, 0, 0, b'', expect_disconnect=True)
def set_safety_mode(self, mode=CarParams.SafetyModel.silent, param=0):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'')

View File

@@ -1,6 +1,5 @@
import binascii
import os
import fcntl
import math
import time
import struct
@@ -12,6 +11,13 @@ from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT
from .constants import McuType, MCU_TYPE_BY_IDCODE, USBPACKET_MAX_SIZE
from .utils import logger
# No fcntl on Windows
try:
import fcntl
except ImportError:
fcntl = None # type: ignore
# No spidev on MacOS/Windows
try:
import spidev
except ImportError:

View File

@@ -8,9 +8,8 @@ PLATFORM=$(uname -s)
echo "installing dependencies"
if [[ $PLATFORM == "Darwin" ]]; then
export HOMEBREW_NO_AUTO_UPDATE=1
brew install --cask gcc-arm-embedded
brew install python3 gcc@13
# pass
:
elif [[ $PLATFORM == "Linux" ]]; then
# for AGNOS since we clear the apt lists
if [[ ! -d /"var/lib/apt/" ]]; then
@@ -19,9 +18,8 @@ elif [[ $PLATFORM == "Linux" ]]; then
sudo apt-get install -y --no-install-recommends \
curl ca-certificates \
make g++ git libnewlib-arm-none-eabi \
make g++ git \
libusb-1.0-0 \
gcc-arm-none-eabi \
python3-dev python3-pip python3-venv
else
echo "WARNING: unsupported platform. skipping apt/brew install."

View File

@@ -11,7 +11,6 @@ def check_signature(p):
def test_dfu(p):
app_mcu_type = p.get_mcu_type()
dfu_serial = p.get_dfu_serial()
p.reset(enter_bootstub=True)
@@ -19,7 +18,7 @@ def test_dfu(p):
assert Panda.wait_for_dfu(dfu_serial, timeout=19), "failed to enter DFU"
dfu = PandaDFU(dfu_serial)
assert dfu.get_mcu_type() == app_mcu_type
assert dfu.get_mcu_type() == McuType.H7
assert dfu_serial in PandaDFU.list()
@@ -38,9 +37,9 @@ def test_known_bootstub(p):
McuType.H7: ["bootstub.panda_h7.bin"],
}
for kb in known_bootstubs[p.get_mcu_type()]:
app_ids = (p.get_mcu_type(), p.get_usb_serial())
assert None not in app_ids
for kb in known_bootstubs[McuType.H7]:
app_serial = p.get_usb_serial()
assert app_serial is not None
p.reset(enter_bootstub=True)
p.reset(enter_bootloader=True)
@@ -57,10 +56,9 @@ def test_known_bootstub(p):
p.connect(claim=False, wait=True)
# check for MCU or serial mismatch
# check for serial mismatch
with Panda(p._serial, claim=False) as np:
bootstub_ids = (np.get_mcu_type(), np.get_usb_serial())
assert app_ids == bootstub_ids
assert np.get_usb_serial() == app_serial
# ensure we can flash app and it jumps to app
p.flash()

View File

@@ -17,8 +17,6 @@ def test_hw_type(p):
"""
hw_type = p.get_type()
mcu_type = p.get_mcu_type()
assert mcu_type is not None
app_uid = p.get_uid()
usb_serial = p.get_usb_serial()
@@ -30,7 +28,6 @@ def test_hw_type(p):
with Panda(p.get_usb_serial()) as pp:
assert pp.bootstub
assert pp.get_type() == hw_type, "Bootstub and app hw type mismatch"
assert pp.get_mcu_type() == mcu_type, "Bootstub and app MCU type mismatch"
assert pp.get_uid() == app_uid
def test_heartbeat(p, panda_jungle):

View File

@@ -1,4 +1,5 @@
import time
import pytest
from panda import Panda
@@ -11,3 +12,33 @@ def test_boot_time(p):
# USB enumeration is slow, so SPI is faster
assert time.monotonic() - st < (1.0 if p.spi else 5.0)
@pytest.mark.panda_expect_can_error
@pytest.mark.test_panda_types((Panda.HW_TYPE_CUATRO, ))
def test_stop_mode(p, panda_jungle):
serial = p.get_usb_serial()
panda_jungle.set_obd(True)
for orientation in (Panda.HARNESS_STATUS_FLIPPED, Panda.HARNESS_STATUS_NORMAL):
panda_jungle.set_harness_orientation(orientation)
time.sleep(0.25) # wait for orientation detection
for wakeup in ("ign", "0", "1", "2"):
panda_jungle.set_ignition(False)
print(f"orientation={orientation} wakeup={wakeup}")
p.enter_stop_mode()
p.close()
# wait for panda to enter stop mode
time.sleep(1.5)
# wake via ignition or CAN activity
if wakeup == "ign":
panda_jungle.set_ignition(True)
else:
panda_jungle.can_send(0x123, b'\x01\x02', int(wakeup))
# panda should reset and come back
assert Panda.wait_for_panda(serial, timeout=10)
p.reconnect()
assert p.health()['uptime'] < 3

View File

@@ -148,6 +148,7 @@ def fixture_panda_setup(request):
init_jungle()
# init panda
assert Panda.wait_for_panda(_panda_serial, timeout=10), "panda not found"
p = Panda(serial=_panda_serial)
p.reset(reconnect=True)

View File

@@ -15,7 +15,6 @@ def recover(s):
def flash(s):
with PandaJungle(s) as p:
p.flash()
return p.get_mcu_type()
# Reset + flash all CI hardware to get it into a consistent state
# * port 1: jungles-under-test
@@ -42,5 +41,4 @@ if __name__ == "__main__":
for s in SERIALS:
assert PandaJungle.wait_for_panda(s, timeout=10)
assert set(PandaJungle.list()) >= SERIALS
mcu_types = list(exc.map(flash, SERIALS, timeout=20))
assert set(mcu_types) == {McuType.H7, }
list(exc.map(flash, SERIALS, timeout=20))

View File

@@ -12,7 +12,7 @@ void can_tx_comms_resume_usb(void) { };
void can_tx_comms_resume_spi(void) { };
#include "health.h"
#include "faults.h"
#include "sys/faults.h"
#include "libc.h"
#include "boards/board_declarations.h"
#include "opendbc/safety/safety.h"

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
: "${CPPCHECK_DIR:=$DIR/cppcheck/}"
if [ ! -d "$CPPCHECK_DIR" ]; then
git clone https://github.com/danmar/cppcheck.git $CPPCHECK_DIR
fi
cd $CPPCHECK_DIR
VERS="2.16.0"
git fetch --all --tags --force
git checkout $VERS
#make clean
make MATCHCOMPILTER=yes CXXFLAGS="-O2" -j8

View File

@@ -10,12 +10,7 @@ YELLOW="\e[1;33m"
RED="\e[1;31m"
NC='\033[0m'
: "${CPPCHECK_DIR:=$DIR/cppcheck/}"
# install cppcheck if missing
if [ -z "${SKIP_CPPCHECK_INSTALL}" ]; then
$DIR/install.sh
fi
: "${CPPCHECK_DIR:=$(python3 -c "import cppcheck; print(cppcheck.DIR)")}"
# ensure checked in coverage table is up to date
cd $DIR