diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 82bc2345..175788fa 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -44,6 +44,22 @@ 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 diff --git a/.gitignore b/.gitignore index 612b14f7..67edc8b6 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ tests/safety/coverage.info *.profraw *.profdata mull.yml + +.claude/ +TASK.md diff --git a/Dockerfile b/Dockerfile index a8a80e97..a41d0c6f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/Jenkinsfile b/Jenkinsfile index d9b1c464..e35e31ee 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -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]) } diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 0c34174c..61e5ba9c 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -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); diff --git a/board/body/can.h b/board/body/can.h index 94010ded..3533b66e 100644 --- a/board/body/can.h +++ b/board/body/can.h @@ -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 diff --git a/board/bootstub_declarations.h b/board/bootstub_declarations.h index 5cdec508..1b958085 100644 --- a/board/bootstub_declarations.h +++ b/board/bootstub_declarations.h @@ -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 ********************** diff --git a/board/critical_declarations.h b/board/critical_declarations.h deleted file mode 100644 index 42211d46..00000000 --- a/board/critical_declarations.h +++ /dev/null @@ -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(); \ - } diff --git a/board/drivers/bootkick.h b/board/drivers/bootkick.h index eab7da3e..4bf99039 100644 --- a/board/drivers/bootkick.h +++ b/board/drivers/bootkick.h @@ -1,4 +1,4 @@ -#include "bootkick_declarations.h" +#include "board/drivers/drivers.h" bool bootkick_reset_triggered = false; diff --git a/board/drivers/bootkick_declarations.h b/board/drivers/bootkick_declarations.h deleted file mode 100644 index 3b55b72a..00000000 --- a/board/drivers/bootkick_declarations.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -extern bool bootkick_reset_triggered; - -void bootkick_tick(bool ignition, bool recent_heartbeat); diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 35c6702e..11524dd1 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -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; diff --git a/board/drivers/can_common_declarations.h b/board/drivers/can_common_declarations.h deleted file mode 100644 index 75580b7b..00000000 --- a/board/drivers/can_common_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/clock_source.h b/board/drivers/clock_source.h index 87d89ad0..4e7ed72c 100644 --- a/board/drivers/clock_source.h +++ b/board/drivers/clock_source.h @@ -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 diff --git a/board/drivers/clock_source_declarations.h b/board/drivers/clock_source_declarations.h deleted file mode 100644 index d95eb961..00000000 --- a/board/drivers/clock_source_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/drivers.h b/board/drivers/drivers.h new file mode 100644 index 00000000..c6150670 --- /dev/null +++ b/board/drivers/drivers.h @@ -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); diff --git a/board/drivers/fan.h b/board/drivers/fan.h index f1041ce8..389fdd3f 100644 --- a/board/drivers/fan.h +++ b/board/drivers/fan.h @@ -1,4 +1,4 @@ -#include "fan_declarations.h" +#include "board/drivers/drivers.h" struct fan_state_t fan_state; diff --git a/board/drivers/fan_declarations.h b/board/drivers/fan_declarations.h deleted file mode 100644 index 3dd3e7cf..00000000 --- a/board/drivers/fan_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index 2aa64e97..4921d531 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -1,4 +1,4 @@ -#include "fdcan_declarations.h" +#include "board/drivers/drivers.h" FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3}; diff --git a/board/drivers/fdcan_declarations.h b/board/drivers/fdcan_declarations.h deleted file mode 100644 index c77d1e31..00000000 --- a/board/drivers/fdcan_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/harness.h b/board/drivers/harness.h index 3ebf109f..d501ab94 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -1,4 +1,4 @@ -#include "harness_declarations.h" +#include "board/drivers/drivers.h" struct harness_t harness; diff --git a/board/drivers/harness_declarations.h b/board/drivers/harness_declarations.h deleted file mode 100644 index 6f4327aa..00000000 --- a/board/drivers/harness_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index 1188a8ec..c404e43d 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -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! diff --git a/board/drivers/interrupts_declarations.h b/board/drivers/interrupts_declarations.h deleted file mode 100644 index 4112453c..00000000 --- a/board/drivers/interrupts_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/registers.h b/board/drivers/registers.h index 92b6b0fa..a4ba8adf 100644 --- a/board/drivers/registers.h +++ b/board/drivers/registers.h @@ -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]; diff --git a/board/drivers/registers_declarations.h b/board/drivers/registers_declarations.h deleted file mode 100644 index 13180117..00000000 --- a/board/drivers/registers_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/simple_watchdog.h b/board/drivers/simple_watchdog.h index f679cb19..7668402f 100644 --- a/board/drivers/simple_watchdog.h +++ b/board/drivers/simple_watchdog.h @@ -1,4 +1,4 @@ -#include "simple_watchdog_declarations.h" +#include "board/drivers/drivers.h" static simple_watchdog_state_t wd_state; diff --git a/board/drivers/simple_watchdog_declarations.h b/board/drivers/simple_watchdog_declarations.h deleted file mode 100644 index 2bda7159..00000000 --- a/board/drivers/simple_watchdog_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/spi.h b/board/drivers/spi.h index d291fe06..283d845e 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -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; diff --git a/board/drivers/spi_declarations.h b/board/drivers/spi_declarations.h deleted file mode 100644 index 23254f0e..00000000 --- a/board/drivers/spi_declarations.h +++ /dev/null @@ -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); diff --git a/board/drivers/uart.h b/board/drivers/uart.h index aeacf84b..250c2ea0 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -1,4 +1,4 @@ -#include "uart_declarations.h" +#include "board/drivers/drivers.h" // ***************************** Definitions ***************************** diff --git a/board/drivers/uart_declarations.h b/board/drivers/uart_declarations.h deleted file mode 100644 index 3096aed2..00000000 --- a/board/drivers/uart_declarations.h +++ /dev/null @@ -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 diff --git a/board/drivers/usb.h b/board/drivers/usb.h index d393d7e4..47c91c99 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -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]; diff --git a/board/drivers/usb_declarations.h b/board/drivers/usb_declarations.h deleted file mode 100644 index 379b162f..00000000 --- a/board/drivers/usb_declarations.h +++ /dev/null @@ -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); diff --git a/board/flasher.h b/board/flasher.h index f816abec..587f7e62 100644 --- a/board/flasher.h +++ b/board/flasher.h @@ -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; diff --git a/board/health.h b/board/health.h index 9040edf5..a7498118 100644 --- a/board/health.h +++ b/board/health.h @@ -1,3 +1,5 @@ +#pragma once + // When changing these structs, python/__init__.py needs to be kept up to date! #define HEALTH_PACKET_VERSION 17 diff --git a/board/jungle/__init__.py b/board/jungle/__init__.py index dfb7d98c..e9e7e96b 100644 --- a/board/jungle/__init__.py +++ b/board/jungle/__init__.py @@ -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 ******************* diff --git a/board/main.c b/board/main.c index c3fdbc68..2bce081e 100644 --- a/board/main.c +++ b/board/main.c @@ -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; } diff --git a/board/main_comms.h b/board/main_comms.h index e0fae6b9..a71dbece 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -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; @@ -95,6 +95,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 +273,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: diff --git a/board/power_saving.h b/board/power_saving.h deleted file mode 100644 index 1e95c951..00000000 --- a/board/power_saving.h +++ /dev/null @@ -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; - } -} diff --git a/board/power_saving_declarations.h b/board/power_saving_declarations.h deleted file mode 100644 index 7a474a55..00000000 --- a/board/power_saving_declarations.h +++ /dev/null @@ -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); diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index 1a3b4136..6fd6eae2 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -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" diff --git a/board/critical.h b/board/sys/critical.h similarity index 90% rename from board/critical.h rename to board/sys/critical.h index ae2d5c0a..70c143f7 100644 --- a/board/critical.h +++ b/board/sys/critical.h @@ -1,4 +1,4 @@ -#include "critical_declarations.h" +#include "board/sys/sys.h" // ********************* Critical section helpers ********************* uint8_t global_critical_depth = 0U; diff --git a/board/faults.h b/board/sys/faults.h similarity index 95% rename from board/faults.h rename to board/sys/faults.h index 0fc9d2c5..00545798 100644 --- a/board/faults.h +++ b/board/sys/faults.h @@ -1,4 +1,4 @@ -#include "faults_declarations.h" +#include "board/sys/sys.h" uint8_t fault_status = FAULT_STATUS_NONE; uint32_t faults = 0U; diff --git a/board/sys/power_saving.h b/board/sys/power_saving.h new file mode 100644 index 00000000..accfdb01 --- /dev/null +++ b/board/sys/power_saving.h @@ -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(); +} diff --git a/board/faults_declarations.h b/board/sys/sys.h similarity index 62% rename from board/faults_declarations.h rename to board/sys/sys.h index 981e2375..fe5527a0 100644 --- a/board/faults_declarations.h +++ b/board/sys/sys.h @@ -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); diff --git a/pyproject.toml b/pyproject.toml index dbb78779..9ce1531f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ dev = [ "mypy", "setuptools", "spidev; platform_system == 'Linux'", + "gcc-arm-none-eabi @ git+https://github.com/commaai/dependencies.git@releases#subdirectory=gcc-arm-none-eabi", ] [build-system] @@ -37,7 +38,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 = "." diff --git a/python/__init__.py b/python/__init__.py index 89093a47..15ef1566 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -112,8 +112,6 @@ 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' @@ -125,7 +123,6 @@ class Panda: HEALTH_STRUCT = struct.Struct(" 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) @@ -605,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 @@ -633,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): """ @@ -657,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'') diff --git a/python/spi.py b/python/spi.py index 4275017e..0e31ce1d 100644 --- a/python/spi.py +++ b/python/spi.py @@ -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: diff --git a/setup.sh b/setup.sh index 042b4978..648432e1 100755 --- a/setup.sh +++ b/setup.sh @@ -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." diff --git a/tests/hitl/1_program.py b/tests/hitl/1_program.py index 09c8b867..bf20e82d 100644 --- a/tests/hitl/1_program.py +++ b/tests/hitl/1_program.py @@ -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() diff --git a/tests/hitl/2_health.py b/tests/hitl/2_health.py index 8fac3e96..f1784b0a 100644 --- a/tests/hitl/2_health.py +++ b/tests/hitl/2_health.py @@ -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): diff --git a/tests/hitl/8_misc.py b/tests/hitl/8_misc.py index 3c47ed49..dad612bf 100644 --- a/tests/hitl/8_misc.py +++ b/tests/hitl/8_misc.py @@ -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 diff --git a/tests/hitl/conftest.py b/tests/hitl/conftest.py index ecc3a455..a8adab07 100644 --- a/tests/hitl/conftest.py +++ b/tests/hitl/conftest.py @@ -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) diff --git a/tests/hitl/reset_jungles.py b/tests/hitl/reset_jungles.py index 09bd04ad..97702d80 100755 --- a/tests/hitl/reset_jungles.py +++ b/tests/hitl/reset_jungles.py @@ -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)) diff --git a/tests/libpanda/panda.c b/tests/libpanda/panda.c index 2d17d64e..5c82a7b7 100644 --- a/tests/libpanda/panda.c +++ b/tests/libpanda/panda.c @@ -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"