This commit is contained in:
infiniteCable2
2025-11-16 13:54:29 +01:00
34 changed files with 218 additions and 367 deletions

7
Jenkinsfile vendored
View File

@@ -32,6 +32,7 @@ export GIT_BRANCH=${env.GIT_BRANCH}
export GIT_COMMIT=${env.GIT_COMMIT} export GIT_COMMIT=${env.GIT_COMMIT}
export PYTHONPATH=${env.TEST_DIR}/../ export PYTHONPATH=${env.TEST_DIR}/../
export PYTHONWARNINGS=error export PYTHONWARNINGS=error
export LOGLEVEL=debug
ln -sf /data/openpilot/opendbc_repo/opendbc /data/opendbc ln -sf /data/openpilot/opendbc_repo/opendbc /data/opendbc
# TODO: this is an agnos issue # TODO: this is an agnos issue
@@ -85,7 +86,7 @@ pipeline {
steps { steps {
timeout(time: 20, unit: 'MINUTES') { timeout(time: 20, unit: 'MINUTES') {
script { script {
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}", "--build-arg CACHEBUST=${env.BUILD_NUMBER} .") dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}", "--build-arg CACHEBUST=${env.GIT_COMMIT} .")
} }
} }
} }
@@ -109,7 +110,7 @@ pipeline {
["build", "scons -j4"], ["build", "scons -j4"],
["flash", "cd scripts/ && ./reflash_internal_panda.py"], ["flash", "cd scripts/ && ./reflash_internal_panda.py"],
["flash jungle", "cd board/jungle && ./flash.py --all"], ["flash jungle", "cd board/jungle && ./flash.py --all"],
["test", "cd tests/hitl && HW_TYPES=10 pytest --durations=0 2*.py [5-9]*.py"], ["test", "cd tests/hitl && pytest --durations=0 2*.py [5-9]*.py"],
]) ])
} }
} }
@@ -121,7 +122,7 @@ pipeline {
["build", "scons -j4"], ["build", "scons -j4"],
["flash", "cd scripts/ && ./reflash_internal_panda.py"], ["flash", "cd scripts/ && ./reflash_internal_panda.py"],
["flash jungle", "cd board/jungle && ./flash.py --all"], ["flash jungle", "cd board/jungle && ./flash.py --all"],
["test", "cd tests/hitl && HW_TYPES=9 pytest --durations=0 2*.py [5-9]*.py"], ["test", "cd tests/hitl && pytest --durations=0 2*.py [5-9]*.py"],
]) ])
} }
} }

View File

@@ -29,10 +29,9 @@ struct board {
const uint8_t led_pin[3]; const uint8_t led_pin[3];
const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM const uint8_t led_pwm_channels[3]; // leave at 0 to disable PWM
const bool has_spi; const bool has_spi;
const uint16_t fan_max_rpm; const bool has_fan;
const uint16_t avdd_mV; const uint16_t avdd_mV;
const uint8_t fan_enable_cooldown_time; const uint8_t fan_enable_cooldown_time;
const uint8_t fan_max_pwm;
board_init init; board_init init;
board_init_bootloader init_bootloader; board_init_bootloader init_bootloader;
board_enable_can_transceiver enable_can_transceiver; board_enable_can_transceiver enable_can_transceiver;

View File

@@ -42,24 +42,6 @@ static void cuatro_set_bootkick(BootState state) {
} }
static void cuatro_set_amp_enabled(bool enabled) { static void cuatro_set_amp_enabled(bool enabled) {
// *** tmp, remove soon ***
static const uint8_t olds[][12] = {
{0x44, 0x00, 0x10, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x14, 0x00, 0x13, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x04, 0x00, 0x30, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x2f, 0x00, 0x14, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x1e, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x26, 0x00, 0x15, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x35, 0x00, 0x32, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x37, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
};
bool is_old = false;
for (uint8_t i = 0U; i < (sizeof(olds) / sizeof(olds[0])); i++) {
is_old |= (memcmp(olds[i], ((uint8_t *)UID_BASE), 12) == 0);
}
if (is_old) set_gpio_output(GPIOA, 5, enabled);
// *** tmp end ***
set_gpio_output(GPIOB, 0, enabled); set_gpio_output(GPIOB, 0, enabled);
} }
@@ -132,8 +114,7 @@ static harness_configuration cuatro_harness_config = {
board board_cuatro = { board board_cuatro = {
.harness_config = &cuatro_harness_config, .harness_config = &cuatro_harness_config,
.has_spi = true, .has_spi = true,
.fan_max_rpm = 12500U, .has_fan = true,
.fan_max_pwm = 99U, // it can go up to 14k RPM, but 99% -> 100% is very non-linear
.avdd_mV = 1800U, .avdd_mV = 1800U,
.fan_enable_cooldown_time = 3U, .fan_enable_cooldown_time = 3U,
.init = cuatro_init, .init = cuatro_init,

View File

@@ -115,8 +115,7 @@ board board_red = {
.set_bootkick = unused_set_bootkick, .set_bootkick = unused_set_bootkick,
.harness_config = &red_harness_config, .harness_config = &red_harness_config,
.has_spi = false, .has_spi = false,
.fan_max_rpm = 0U, .has_fan = false,
.fan_max_pwm = 100U,
.avdd_mV = 3300U, .avdd_mV = 3300U,
.fan_enable_cooldown_time = 0U, .fan_enable_cooldown_time = 0U,
.init = red_init, .init = red_init,

View File

@@ -155,8 +155,7 @@ static harness_configuration tres_harness_config = {
board board_tres = { board board_tres = {
.harness_config = &tres_harness_config, .harness_config = &tres_harness_config,
.has_spi = true, .has_spi = true,
.fan_max_rpm = 6600U, .has_fan = true,
.fan_max_pwm = 100U,
.avdd_mV = 1800U, .avdd_mV = 1800U,
.fan_enable_cooldown_time = 3U, .fan_enable_cooldown_time = 3U,
.init = tres_init, .init = tres_init,

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#include "can_declarations.h"
static const uint8_t PANDA_CAN_CNT = 3U; #define PANDA_CAN_CNT 3U
static const uint8_t PANDA_BUS_CNT = 3U;
static const unsigned char dlc_to_len[] = {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 12U, 16U, 20U, 24U, 32U, 48U, 64U}; #include "opendbc/safety/can.h"

View File

@@ -1,24 +0,0 @@
#pragma once
// bump this when changing the CAN packet
#define CAN_PACKET_VERSION 4
#define CANPACKET_HEAD_SIZE 6U
#define CANPACKET_DATA_SIZE_MAX 64U
typedef struct {
unsigned char fd : 1;
unsigned char bus : 3;
unsigned char data_len_code : 4; // lookup length with dlc_to_len
unsigned char rejected : 1;
unsigned char returned : 1;
unsigned char extended : 1;
unsigned int addr : 29;
unsigned char checksum;
unsigned char data[CANPACKET_DATA_SIZE_MAX];
} __attribute__((packed, aligned(4))) CANPacket_t;
#define GET_BUS(msg) ((msg)->bus)
#define GET_LEN(msg) (dlc_to_len[(msg)->data_len_code])
#define GET_ADDR(msg) ((msg)->addr)

View File

@@ -5,15 +5,13 @@ uint32_t safety_rx_invalid = 0;
uint32_t tx_buffer_overflow = 0; uint32_t tx_buffer_overflow = 0;
uint32_t rx_buffer_overflow = 0; uint32_t rx_buffer_overflow = 0;
can_health_t can_health[CAN_HEALTH_ARRAY_SIZE] = {{0}, {0}, {0}}; can_health_t can_health[PANDA_CAN_CNT] = {{0}, {0}, {0}};
// Ignition detected from CAN meessages // Ignition detected from CAN meessages
bool ignition_can = false; bool ignition_can = false;
uint32_t ignition_can_cnt = 0U; uint32_t ignition_can_cnt = 0U;
int can_live = 0; bool can_silent = true;
int pending_can_live = 0;
int can_silent = ALL_CAN_SILENT;
bool can_loopback = false; bool can_loopback = false;
// ********************* instantiate queues ********************* // ********************* instantiate queues *********************
@@ -39,7 +37,7 @@ can_buffer(tx3_q, CAN_TX_BUFFER_SIZE)
// FIXME: // FIXME:
// cppcheck-suppress misra-c2012-9.3 // cppcheck-suppress misra-c2012-9.3
can_ring *can_queues[CAN_QUEUES_ARRAY_SIZE] = {&can_tx1_q, &can_tx2_q, &can_tx3_q}; can_ring *can_queues[PANDA_CAN_CNT] = {&can_tx1_q, &can_tx2_q, &can_tx3_q};
// ********************* interrupt safe queue ********************* // ********************* interrupt safe queue *********************
bool can_pop(can_ring *q, CANPacket_t *elem) { bool can_pop(can_ring *q, CANPacket_t *elem) {
@@ -130,15 +128,15 @@ void can_clear(can_ring *q) {
// Helpers // Helpers
// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3 // Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3
bus_config_t bus_config[BUS_CONFIG_ARRAY_SIZE] = { bus_config_t bus_config[PANDA_CAN_CNT] = {
{ .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, { .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
{ .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, { .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
{ .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, { .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
{ .bus_lookup = 0xFFU, .can_num_lookup = 0xFFU, .forwarding_bus = -1, .can_speed = 333U, .can_data_speed = 333U, .canfd_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false },
}; };
void can_init_all(void) { void can_init_all(void) {
for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) {
bus_config[i].canfd_enabled = false;
can_clear(can_queues[i]); can_clear(can_queues[i]);
(void)can_init(i); (void)can_init(i);
} }
@@ -158,20 +156,18 @@ void can_set_forwarding(uint8_t from, uint8_t to) {
#endif #endif
void ignition_can_hook(CANPacket_t *msg) { void ignition_can_hook(CANPacket_t *msg) {
int bus = GET_BUS(msg); if (msg->bus == 0U) {
if (bus == 0) {
int addr = GET_ADDR(msg);
int len = GET_LEN(msg); int len = GET_LEN(msg);
// GM exception // GM exception
if ((addr == 0x1F1) && (len == 8)) { if ((msg->addr == 0x1F1U) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request) // SystemPowerMode (2=Run, 3=Crank Request)
ignition_can = (msg->data[0] & 0x2U) != 0U; ignition_can = (msg->data[0] & 0x2U) != 0U;
ignition_can_cnt = 0U; ignition_can_cnt = 0U;
} }
// Rivian R1S/T GEN1 exception // Rivian R1S/T GEN1 exception
if ((addr == 0x152) && (len == 8)) { if ((msg->addr == 0x152U) && (len == 8)) {
// 0x152 overlaps with Subaru pre-global which has this bit as the high beam // 0x152 overlaps with Subaru pre-global which has this bit as the high beam
int counter = msg->data[1] & 0xFU; // max is only 14 int counter = msg->data[1] & 0xFU; // max is only 14
@@ -185,7 +181,7 @@ void ignition_can_hook(CANPacket_t *msg) {
} }
// Tesla Model 3/Y exception // Tesla Model 3/Y exception
if ((addr == 0x221) && (len == 8)) { if ((msg->addr == 0x221U) && (len == 8)) {
// 0x221 overlaps with Rivian which has random data on byte 0 // 0x221 overlaps with Rivian which has random data on byte 0
int counter = msg->data[6] >> 4; int counter = msg->data[6] >> 4;
@@ -200,7 +196,7 @@ void ignition_can_hook(CANPacket_t *msg) {
} }
// Mazda exception // Mazda exception
if ((addr == 0x9E) && (len == 8)) { if ((msg->addr == 0x9EU) && (len == 8)) {
ignition_can = (msg->data[0] >> 5) == 0x6U; ignition_can = (msg->data[0] >> 5) == 0x6U;
ignition_can_cnt = 0U; ignition_can_cnt = 0U;
} }
@@ -240,7 +236,7 @@ bool can_check_checksum(CANPacket_t *packet) {
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) { void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) {
if (skip_tx_hook || safety_tx_hook(to_push) != 0) { if (skip_tx_hook || safety_tx_hook(to_push) != 0) {
if (bus_number < PANDA_BUS_CNT) { if (bus_number < PANDA_CAN_CNT) {
// add CAN packet to send queue // add CAN packet to send queue
tx_buffer_overflow += can_push(can_queues[bus_number], to_push) ? 0U : 1U; tx_buffer_overflow += can_push(can_queues[bus_number], to_push) ? 0U : 1U;
process_can(CAN_NUM_FROM_BUS_NUM(bus_number)); process_can(CAN_NUM_FROM_BUS_NUM(bus_number));

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "board/can.h"
typedef struct { typedef struct {
volatile uint32_t w_ptr; volatile uint32_t w_ptr;
volatile uint32_t r_ptr; volatile uint32_t r_ptr;
@@ -24,19 +26,13 @@ extern uint32_t safety_rx_invalid;
extern uint32_t tx_buffer_overflow; extern uint32_t tx_buffer_overflow;
extern uint32_t rx_buffer_overflow; extern uint32_t rx_buffer_overflow;
#define CAN_HEALTH_ARRAY_SIZE 3 extern can_health_t can_health[PANDA_CAN_CNT];
extern can_health_t can_health[CAN_HEALTH_ARRAY_SIZE];
// Ignition detected from CAN meessages // Ignition detected from CAN meessages
extern bool ignition_can; extern bool ignition_can;
extern uint32_t ignition_can_cnt; extern uint32_t ignition_can_cnt;
#define ALL_CAN_SILENT 0xFF extern bool can_silent;
#define ALL_CAN_LIVE 0
extern int can_live;
extern int pending_can_live;
extern int can_silent;
extern bool can_loopback; extern bool can_loopback;
// ******************* functions prototypes ********************* // ******************* functions prototypes *********************
@@ -44,8 +40,7 @@ bool can_init(uint8_t can_number);
void process_can(uint8_t can_number); void process_can(uint8_t can_number);
// ********************* instantiate queues ********************* // ********************* instantiate queues *********************
#define CAN_QUEUES_ARRAY_SIZE 3 extern can_ring *can_queues[PANDA_CAN_CNT];
extern can_ring *can_queues[CAN_QUEUES_ARRAY_SIZE];
// helpers // 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 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)
@@ -55,20 +50,7 @@ extern can_ring *can_queues[CAN_QUEUES_ARRAY_SIZE];
bool can_pop(can_ring *q, CANPacket_t *elem); bool can_pop(can_ring *q, CANPacket_t *elem);
bool can_push(can_ring *q, const CANPacket_t *elem); bool can_push(can_ring *q, const CANPacket_t *elem);
uint32_t can_slots_empty(const can_ring *q); uint32_t can_slots_empty(const can_ring *q);
extern bus_config_t bus_config[PANDA_CAN_CNT];
// assign CAN numbering
// bus num: CAN Bus numbers in panda, sent to/from USB
// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for but 1)
// cans: Look up MCU can interface from bus number
// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc);
// bus_lookup: Translates from 'can number' to 'bus number'.
// can_num_lookup: Translates from 'bus number' to 'can number'.
// forwarding bus: If >= 0, forward all messages from this bus to the specified bus.
// Helpers
// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3
#define BUS_CONFIG_ARRAY_SIZE 4
extern bus_config_t bus_config[BUS_CONFIG_ARRAY_SIZE];
#define CANIF_FROM_CAN_NUM(num) (cans[num]) #define CANIF_FROM_CAN_NUM(num) (cans[num])
#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup) #define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup)

View File

@@ -5,10 +5,13 @@ struct fan_state_t fan_state;
static const uint8_t FAN_TICK_FREQ = 8U; static const uint8_t FAN_TICK_FREQ = 8U;
void fan_set_power(uint8_t percentage) { void fan_set_power(uint8_t percentage) {
fan_state.target_rpm = ((current_board->fan_max_rpm * CLAMP(percentage, 0U, 100U)) / 100U); if (percentage > 0U) {
fan_state.power = CLAMP(percentage, 20U, 100U);
} else {
fan_state.power = 0U;
}
} }
void llfan_init(void);
void fan_init(void) { void fan_init(void) {
fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ;
llfan_init(); llfan_init();
@@ -16,9 +19,7 @@ void fan_init(void) {
// Call this at FAN_TICK_FREQ // Call this at FAN_TICK_FREQ
void fan_tick(void) { void fan_tick(void) {
const float FAN_I = 6.5f; if (current_board->has_fan) {
if (current_board->fan_max_rpm > 0U) {
// Measure fan RPM // Measure fan RPM
uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation
fan_state.tach_counter = 0U; fan_state.tach_counter = 0U;
@@ -31,8 +32,8 @@ void fan_tick(void) {
print("\n"); print("\n");
#endif #endif
// Cooldown counter // Cooldown counter to prevent noise on tachometer line.
if (fan_state.target_rpm > 0U) { if (fan_state.power > 0U) {
fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ;
} else { } else {
if (fan_state.cooldown_counter > 0U) { if (fan_state.cooldown_counter > 0U) {
@@ -40,18 +41,8 @@ void fan_tick(void) {
} }
} }
// Update controller
if (fan_state.target_rpm == 0U) {
fan_state.error_integral = 0.0f;
} else {
float error = (fan_state.target_rpm - fan_rpm_fast) / ((float) current_board->fan_max_rpm);
fan_state.error_integral += FAN_I * error;
}
fan_state.error_integral = CLAMP(fan_state.error_integral, 0U, current_board->fan_max_pwm);
fan_state.power = fan_state.error_integral;
// Set PWM and enable line // Set PWM and enable line
pwm_set(TIM3, 3, fan_state.power); pwm_set(TIM3, 3, fan_state.power);
current_board->set_fan_enabled((fan_state.target_rpm > 0U) || (fan_state.cooldown_counter > 0U)); current_board->set_fan_enabled((fan_state.power > 0U) || (fan_state.cooldown_counter > 0U));
} }
} }

View File

@@ -3,7 +3,6 @@
struct fan_state_t { struct fan_state_t {
uint16_t tach_counter; uint16_t tach_counter;
uint16_t rpm; uint16_t rpm;
uint16_t target_rpm;
uint8_t power; uint8_t power;
float error_integral; float error_integral;
uint8_t cooldown_counter; uint8_t cooldown_counter;

View File

@@ -1,6 +1,6 @@
#include "fdcan_declarations.h" #include "fdcan_declarations.h"
FDCAN_GlobalTypeDef *cans[CANS_ARRAY_SIZE] = {FDCAN1, FDCAN2, FDCAN3}; FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT] = {FDCAN1, FDCAN2, FDCAN3};
static bool can_set_speed(uint8_t can_number) { static bool can_set_speed(uint8_t can_number) {
bool ret = true; bool ret = true;
@@ -13,7 +13,7 @@ static bool can_set_speed(uint8_t can_number) {
bus_config[bus_number].can_data_speed, bus_config[bus_number].can_data_speed,
bus_config[bus_number].canfd_non_iso, bus_config[bus_number].canfd_non_iso,
can_loopback, can_loopback,
(unsigned int)(can_silent) & (1U << can_number) can_silent
); );
return ret; return ret;
} }
@@ -32,7 +32,7 @@ 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 update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) {
uint8_t can_irq_number[3][2] = { uint8_t can_irq_number[PANDA_CAN_CNT][2] = {
{ FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn },
{ FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn },
{ FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn },
@@ -63,7 +63,6 @@ void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) {
can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate;
can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate;
if (ir_reg != 0U) { if (ir_reg != 0U) {
// Clear error interrupts // Clear error interrupts
FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L);
@@ -158,17 +157,13 @@ void can_rx(uint8_t can_number) {
// Clear all new messages from Rx FIFO 0 // Clear all new messages from Rx FIFO 0
FDCANx->IR |= FDCAN_IR_RF0N; FDCANx->IR |= FDCAN_IR_RF0N;
while((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) { while ((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0U) {
can_health[can_number].total_rx_cnt += 1U; can_health[can_number].total_rx_cnt += 1U;
// can is live
pending_can_live = 1;
// get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1)
uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU); uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3FU);
// Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet)
if((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { if ((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) {
rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U);
can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost
} }

View File

@@ -1,26 +1,19 @@
#pragma once #pragma once
// IRQs: FDCAN1_IT0, FDCAN1_IT1 #include "board/can.h"
// FDCAN2_IT0, FDCAN2_IT1
// FDCAN3_IT0, FDCAN3_IT1
typedef struct { typedef struct {
volatile uint32_t header[2]; volatile uint32_t header[2];
volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U]; volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U];
} canfd_fifo; } canfd_fifo;
#define CANS_ARRAY_SIZE 3 extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
extern FDCAN_GlobalTypeDef *cans[CANS_ARRAY_SIZE];
#define CAN_ACK_ERROR 3U #define CAN_ACK_ERROR 3U
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number); 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 update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
// ***************************** CAN *****************************
// FDFDCANx_IT1 IRQ Handler (TX)
void process_can(uint8_t can_number); void process_can(uint8_t can_number);
// FDFDCANx_IT0 IRQ Handler (RX and errors)
// blink blue when we are receiving CAN messages
void can_rx(uint8_t can_number); void can_rx(uint8_t can_number);
bool can_init(uint8_t can_number); bool can_init(uint8_t can_number);

View File

@@ -3,7 +3,7 @@
#define LED_GREEN 1U #define LED_GREEN 1U
#define LED_BLUE 2U #define LED_BLUE 2U
#define LED_PWM_POWER 5U #define LED_PWM_POWER 2U
void led_set(uint8_t color, bool enabled) { void led_set(uint8_t color, bool enabled) {
if (color < 3U) { if (color < 3U) {

View File

@@ -1,4 +1,4 @@
#define PWM_COUNTER_OVERFLOW 2000U // To get ~50kHz #define PWM_COUNTER_OVERFLOW 4800U // To get ~25kHz
// TODO: Implement for 32-bit timers // TODO: Implement for 32-bit timers

View File

@@ -3,10 +3,8 @@
#include "board/drivers/spi_declarations.h" #include "board/drivers/spi_declarations.h"
#include "board/crc.h" #include "board/crc.h"
#define SPI_BUF_SIZE 2048U uint8_t spi_buf_rx[SPI_BUF_SIZE];
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 uint8_t spi_buf_tx[SPI_BUF_SIZE];
__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE];
__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE];
uint16_t spi_error_count = 0; uint16_t spi_error_count = 0;
@@ -202,7 +200,7 @@ void spi_rx_done(void) {
llspi_miso_dma(spi_buf_tx, response_len); llspi_miso_dma(spi_buf_tx, response_len);
spi_state = next_rx_state; spi_state = next_rx_state;
if (!checksum_valid && (spi_error_count < UINT16_MAX)) { if (!checksum_valid) {
spi_error_count += 1U; spi_error_count += 1U;
} }
} }

View File

@@ -8,7 +8,7 @@
// in a tight loop, plus some buffer // in a tight loop, plus some buffer
#define SPI_IRQ_RATE 16000U #define SPI_IRQ_RATE 16000U
#define SPI_BUF_SIZE 2048U #define SPI_BUF_SIZE 4096U
// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 // 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_rx[SPI_BUF_SIZE];
__attribute__((section(".sram12"))) extern uint8_t spi_buf_tx[SPI_BUF_SIZE]; __attribute__((section(".sram12"))) extern uint8_t spi_buf_tx[SPI_BUF_SIZE];

View File

@@ -11,20 +11,10 @@
#define FAULT_INTERRUPT_RATE_CAN_2 (1UL << 3) #define FAULT_INTERRUPT_RATE_CAN_2 (1UL << 3)
#define FAULT_INTERRUPT_RATE_CAN_3 (1UL << 4) #define FAULT_INTERRUPT_RATE_CAN_3 (1UL << 4)
#define FAULT_INTERRUPT_RATE_TACH (1UL << 5) #define FAULT_INTERRUPT_RATE_TACH (1UL << 5)
#define FAULT_INTERRUPT_RATE_GMLAN (1UL << 6) // deprecated
#define FAULT_INTERRUPT_RATE_INTERRUPTS (1UL << 7) #define FAULT_INTERRUPT_RATE_INTERRUPTS (1UL << 7)
#define FAULT_INTERRUPT_RATE_SPI_DMA (1UL << 8) #define FAULT_INTERRUPT_RATE_SPI_DMA (1UL << 8)
#define FAULT_INTERRUPT_RATE_SPI_CS (1UL << 9)
#define FAULT_INTERRUPT_RATE_UART_1 (1UL << 10)
#define FAULT_INTERRUPT_RATE_UART_2 (1UL << 11)
#define FAULT_INTERRUPT_RATE_UART_3 (1UL << 12)
#define FAULT_INTERRUPT_RATE_UART_5 (1UL << 13)
#define FAULT_INTERRUPT_RATE_UART_DMA (1UL << 14)
#define FAULT_INTERRUPT_RATE_USB (1UL << 15) #define FAULT_INTERRUPT_RATE_USB (1UL << 15)
#define FAULT_INTERRUPT_RATE_TIM1 (1UL << 16)
#define FAULT_INTERRUPT_RATE_TIM3 (1UL << 17)
#define FAULT_REGISTER_DIVERGENT (1UL << 18) #define FAULT_REGISTER_DIVERGENT (1UL << 18)
#define FAULT_INTERRUPT_RATE_KLINE_INIT (1UL << 19)
#define FAULT_INTERRUPT_RATE_CLOCK_SOURCE (1UL << 20) #define FAULT_INTERRUPT_RATE_CLOCK_SOURCE (1UL << 20)
#define FAULT_INTERRUPT_RATE_TICK (1UL << 21) #define FAULT_INTERRUPT_RATE_TICK (1UL << 21)
#define FAULT_INTERRUPT_RATE_EXTI (1UL << 22) #define FAULT_INTERRUPT_RATE_EXTI (1UL << 22)

View File

@@ -48,7 +48,7 @@ class PandaJungle(Panda):
@classmethod @classmethod
def spi_connect(cls, serial, ignore_version=False): def spi_connect(cls, serial, ignore_version=False):
return None, None, None, None, None return None, None, None, None
def flash(self, fn=None, code=None, reconnect=True): def flash(self, fn=None, code=None, reconnect=True):
if not fn: if not fn:

View File

@@ -175,7 +175,7 @@ int main(void) {
print("**** INTERRUPTS ON ****\n"); print("**** INTERRUPTS ON ****\n");
enable_interrupts(); enable_interrupts();
can_silent = ALL_CAN_LIVE; can_silent = false;
set_safety_hooks(SAFETY_ALLOUTPUT, 0U); set_safety_hooks(SAFETY_ALLOUTPUT, 0U);
can_init_all(); can_init_all();

View File

@@ -189,7 +189,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break; break;
// **** 0xde: set can bitrate // **** 0xde: set can bitrate
case 0xde: case 0xde:
if ((req->param1 < PANDA_BUS_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) {
bus_config[req->param1].can_speed = req->param2; bus_config[req->param1].can_speed = req->param2;
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
UNUSED(ret); UNUSED(ret);
@@ -212,7 +212,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
if (req->param1 == 0xFFFFU) { if (req->param1 == 0xFFFFU) {
print("Clearing CAN Rx queue\n"); print("Clearing CAN Rx queue\n");
can_clear(&can_rx_q); can_clear(&can_rx_q);
} else if (req->param1 < PANDA_BUS_CNT) { } else if (req->param1 < PANDA_CAN_CNT) {
print("Clearing CAN Tx queue\n"); print("Clearing CAN Tx queue\n");
can_clear(can_queues[req->param1]); can_clear(can_queues[req->param1]);
} else { } else {
@@ -225,7 +225,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break; break;
// **** 0xf5: Set CAN silent mode // **** 0xf5: Set CAN silent mode
case 0xf5: case 0xf5:
can_silent = (req->param1 > 0U) ? ALL_CAN_SILENT : ALL_CAN_LIVE; can_silent = (req->param1 > 0U);
can_init_all(); can_init_all();
break; break;
// **** 0xf7: enable/disable header pin by number // **** 0xf7: enable/disable header pin by number

View File

@@ -55,12 +55,12 @@ void set_safety_mode(uint16_t mode, uint16_t param) {
case SAFETY_SILENT: case SAFETY_SILENT:
set_intercept_relay(false, false); set_intercept_relay(false, false);
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_SILENT; can_silent = true;
break; break;
case SAFETY_NOOUTPUT: case SAFETY_NOOUTPUT:
set_intercept_relay(false, false); set_intercept_relay(false, false);
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_LIVE; can_silent = false;
break; break;
case SAFETY_ELM327: case SAFETY_ELM327:
set_intercept_relay(false, false); set_intercept_relay(false, false);
@@ -75,14 +75,14 @@ void set_safety_mode(uint16_t mode, uint16_t param) {
} else { } else {
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
} }
can_silent = ALL_CAN_LIVE; can_silent = false;
break; break;
default: default:
set_intercept_relay(true, false); set_intercept_relay(true, false);
heartbeat_counter = 0U; heartbeat_counter = 0U;
heartbeat_lost = false; heartbeat_lost = false;
current_board->set_can_mode(CAN_MODE_NORMAL); current_board->set_can_mode(CAN_MODE_NORMAL);
can_silent = ALL_CAN_LIVE; can_silent = false;
break; break;
} }
can_init_all(); can_init_all();
@@ -118,6 +118,7 @@ static void tick_handler(void) {
static uint32_t controls_allowed_countdown = 0; static uint32_t controls_allowed_countdown = 0;
static uint8_t prev_harness_status = HARNESS_STATUS_NC; static uint8_t prev_harness_status = HARNESS_STATUS_NC;
static uint8_t loop_counter = 0U; static uint8_t loop_counter = 0U;
static bool relay_malfunction_prev = false;
if (TICK_TIMER->SR != 0U) { if (TICK_TIMER->SR != 0U) {
@@ -130,6 +131,15 @@ static void tick_handler(void) {
simple_watchdog_kick(); simple_watchdog_kick();
sound_tick(); sound_tick();
if (relay_malfunction_prev != relay_malfunction) {
if (relay_malfunction) {
fault_occurred(FAULT_RELAY_MALFUNCTION);
} else {
fault_recovered(FAULT_RELAY_MALFUNCTION);
}
}
relay_malfunction_prev = relay_malfunction;
// re-init everything that uses harness status // re-init everything that uses harness status
if (harness.status != prev_harness_status) { if (harness.status != prev_harness_status) {
prev_harness_status = harness.status; prev_harness_status = harness.status;
@@ -143,14 +153,7 @@ static void tick_handler(void) {
// decimated to 1Hz // decimated to 1Hz
if (loop_counter == 0U) { if (loop_counter == 0U) {
can_live = pending_can_live;
//puth(usart1_dma); print(" "); puth(DMA2_Stream5->M0AR); print(" "); puth(DMA2_Stream5->NDTR); print("\n"); //puth(usart1_dma); print(" "); puth(DMA2_Stream5->M0AR); print(" "); puth(DMA2_Stream5->NDTR); print("\n");
// reset this every 16th pass
if ((uptime_cnt & 0xFU) == 0U) {
pending_can_live = 0;
}
#ifdef DEBUG #ifdef DEBUG
print("** blink "); print("** blink ");
print("rx:"); puth4(can_rx_q.r_ptr); print("-"); puth4(can_rx_q.w_ptr); print(" "); print("rx:"); puth4(can_rx_q.r_ptr); print("-"); puth4(can_rx_q.w_ptr); print(" ");
@@ -303,7 +306,7 @@ int main(void) {
microsecond_timer_init(); microsecond_timer_init();
current_board->set_siren(false); current_board->set_siren(false);
if (current_board->fan_max_rpm > 0U) { if (current_board->has_fan) {
fan_init(); fan_init();
} }

View File

@@ -210,13 +210,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break; break;
// **** 0xdb: set OBD CAN multiplexing mode // **** 0xdb: set OBD CAN multiplexing mode
case 0xdb: case 0xdb:
if (req->param1 == 1U) { current_board->set_can_mode((req->param1 == 1U) ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL);
// Enable OBD CAN
current_board->set_can_mode(CAN_MODE_OBD_CAN2);
} else {
// Disable OBD CAN
current_board->set_can_mode(CAN_MODE_NORMAL);
}
break; break;
// **** 0xdc: set safety mode // **** 0xdc: set safety mode
case 0xdc: case 0xdc:
@@ -231,7 +225,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
break; break;
// **** 0xde: set can bitrate // **** 0xde: set can bitrate
case 0xde: case 0xde:
if ((req->param1 < PANDA_BUS_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { if ((req->param1 < PANDA_CAN_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) {
bus_config[req->param1].can_speed = req->param2; bus_config[req->param1].can_speed = req->param2;
bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1));
UNUSED(ret); UNUSED(ret);
@@ -282,7 +276,7 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
if (req->param1 == 0xFFFFU) { if (req->param1 == 0xFFFFU) {
print("Clearing CAN Rx queue\n"); print("Clearing CAN Rx queue\n");
can_clear(&can_rx_q); can_clear(&can_rx_q);
} else if (req->param1 < PANDA_BUS_CNT) { } else if (req->param1 < PANDA_CAN_CNT) {
print("Clearing CAN Tx queue\n"); print("Clearing CAN Tx queue\n");
can_clear(can_queues[req->param1]); can_clear(can_queues[req->param1]);
} else { } else {

View File

@@ -104,33 +104,12 @@ void sound_init(void) {
REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA) REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
// *** tmp, remove soon ***
static const uint8_t olds[][12] = {
{0x44, 0x00, 0x10, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x14, 0x00, 0x13, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x04, 0x00, 0x30, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x2f, 0x00, 0x14, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x1e, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x26, 0x00, 0x15, 0x00, 0x19, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x35, 0x00, 0x32, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
{0x37, 0x00, 0x2f, 0x00, 0x18, 0x51, 0x32, 0x34, 0x39, 0x37, 0x37, 0x30},
};
bool is_old = false;
for (uint8_t i = 0U; i < (sizeof(olds) / sizeof(olds[0])); i++) {
is_old |= (memcmp(olds[i], ((uint8_t *)UID_BASE), 12) == 0);
}
// *** tmp end ***
// Init DAC // Init DAC
DAC1->DHR12R1 = (1UL << 11); DAC1->DHR12R1 = (1UL << 11);
if (!is_old) DAC1->DHR12R2 = (1UL << 11); DAC1->DHR12R2 = (1UL << 11);
register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU);
register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU);
if (is_old) { register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2);
register_set_bits(&DAC1->CR, DAC_CR_EN1);
} else {
register_set_bits(&DAC1->CR, DAC_CR_EN1 | DAC_CR_EN2);
}
// Setup DMAMUX (DAC_CH1_DMA as input) // Setup DMAMUX (DAC_CH1_DMA as input)
register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk);

View File

@@ -30,6 +30,7 @@ dev = [
"mypy", "mypy",
"setuptools", "setuptools",
"spidev; platform_system == 'Linux'", "spidev; platform_system == 'Linux'",
"spidev2; platform_system == 'Linux'",
] ]
[build-system] [build-system]

View File

@@ -23,7 +23,7 @@ __version__ = '0.0.10'
CANPACKET_HEAD_SIZE = 0x6 CANPACKET_HEAD_SIZE = 0x6
DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64] DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]
LEN_TO_DLC = {length: dlc for (dlc, length) in enumerate(DLC_TO_LEN)} LEN_TO_DLC = {length: dlc for (dlc, length) in enumerate(DLC_TO_LEN)}
PANDA_BUS_CNT = 3 PANDA_CAN_CNT = 3
def calculate_checksum(data): def calculate_checksum(data):
@@ -32,16 +32,13 @@ def calculate_checksum(data):
res ^= b res ^= b
return res return res
def pack_can_buffer(arr, fd=False): def pack_can_buffer(arr, chunk=False, fd=False):
snds = [b''] snds = [bytearray(), ]
for address, dat, bus in arr: for address, dat, bus in arr:
assert len(dat) in LEN_TO_DLC
#logger.debug(" W 0x%x: 0x%s", address, dat.hex())
extended = 1 if address >= 0x800 else 0 extended = 1 if address >= 0x800 else 0
data_len_code = LEN_TO_DLC[len(dat)] data_len_code = LEN_TO_DLC[len(dat)]
header = bytearray(CANPACKET_HEAD_SIZE) header = bytearray(CANPACKET_HEAD_SIZE)
word_4b = address << 3 | extended << 2 word_4b = (address << 3) | (extended << 2)
header[0] = (data_len_code << 4) | (bus << 1) | int(fd) header[0] = (data_len_code << 4) | (bus << 1) | int(fd)
header[1] = word_4b & 0xFF header[1] = word_4b & 0xFF
header[2] = (word_4b >> 8) & 0xFF header[2] = (word_4b >> 8) & 0xFF
@@ -49,9 +46,10 @@ def pack_can_buffer(arr, fd=False):
header[4] = (word_4b >> 24) & 0xFF header[4] = (word_4b >> 24) & 0xFF
header[5] = calculate_checksum(header[:5] + dat) header[5] = calculate_checksum(header[:5] + dat)
snds[-1] += header + dat snds[-1].extend(header)
if len(snds[-1]) > 256: # Limit chunks to 256 bytes snds[-1].extend(dat)
snds.append(b'') if chunk and len(snds[-1]) > 256:
snds.append(bytearray())
return snds return snds
@@ -133,7 +131,7 @@ class Panda:
MAX_FAN_RPMs = { MAX_FAN_RPMs = {
HW_TYPE_TRES: 6600, HW_TYPE_TRES: 6600,
HW_TYPE_CUATRO: 12500, HW_TYPE_CUATRO: 5000,
} }
HARNESS_STATUS_NC = 0 HARNESS_STATUS_NC = 0
@@ -198,9 +196,9 @@ class Panda:
self._handle = None self._handle = None
while self._handle is None: while self._handle is None:
# try USB first, then SPI # try USB first, then SPI
self._context, self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim, no_error=wait) self._context, self._handle, serial, self.bootstub = self.usb_connect(self._connect_serial, claim=claim, no_error=wait)
if self._handle is None: if self._handle is None:
self._context, self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial) self._context, self._handle, serial, self.bootstub = self.spi_connect(self._connect_serial)
if not wait: if not wait:
break break
@@ -227,11 +225,11 @@ class Panda:
self.can_reset_communications() self.can_reset_communications()
# disable automatic CAN-FD switching # disable automatic CAN-FD switching
for bus in range(PANDA_BUS_CNT): for bus in range(PANDA_CAN_CNT):
self.set_canfd_auto(bus, False) self.set_canfd_auto(bus, False)
# set CAN speed # set CAN speed
for bus in range(PANDA_BUS_CNT): for bus in range(PANDA_CAN_CNT):
self.set_can_speed_kbps(bus, self._can_speed_kbps) self.set_can_speed_kbps(bus, self._can_speed_kbps)
@property @property
@@ -240,49 +238,33 @@ class Panda:
@classmethod @classmethod
def spi_connect(cls, serial, ignore_version=False): def spi_connect(cls, serial, ignore_version=False):
# get UID to confirm slave is present and up
handle = None
spi_serial = None
bootstub = None
spi_version = None
try: try:
handle = PandaSpiHandle() handle = PandaSpiHandle()
dat = handle.get_protocol_version()
# connect by protcol version
try:
dat = handle.get_protocol_version()
spi_serial = binascii.hexlify(dat[:12]).decode()
pid = dat[13]
if pid not in (0xcc, 0xee):
raise PandaSpiException("invalid bootstub status")
bootstub = pid == 0xee
spi_version = dat[14]
except PandaSpiException:
# fallback, we'll raise a protocol mismatch below
dat = handle.controlRead(Panda.REQUEST_IN, 0xc3, 0, 0, 12, timeout=100)
spi_serial = binascii.hexlify(dat).decode()
bootstub = Panda.flasher_present(handle)
spi_version = 0
except PandaSpiException: except PandaSpiException:
pass return None, None, None, False
# no connection or wrong panda spi_serial = binascii.hexlify(dat[:12]).decode()
if None in (spi_serial, bootstub) or (serial is not None and (spi_serial != serial)): pid = dat[13]
handle = None if pid not in (0xcc, 0xee):
spi_serial = None raise PandaProtocolMismatch(f"invalid bootstub status ({pid=}). reflash panda")
bootstub = False bootstub = pid == 0xee
spi_version = dat[14]
# did we get the right panda?
if serial is not None and spi_serial != serial:
return None, None, None, False
# ensure our protocol version matches the panda # ensure our protocol version matches the panda
if handle is not None and not ignore_version: if (not ignore_version) and spi_version != handle.PROTOCOL_VERSION:
if spi_version != handle.PROTOCOL_VERSION: raise PandaProtocolMismatch(f"panda protocol mismatch: expected {handle.PROTOCOL_VERSION}, got {spi_version}. reflash panda")
err = f"panda protocol mismatch: expected {handle.PROTOCOL_VERSION}, got {spi_version}. reflash panda"
raise PandaProtocolMismatch(err)
return None, handle, spi_serial, bootstub, None # got a device and all good
return None, handle, spi_serial, bootstub
@classmethod @classmethod
def usb_connect(cls, serial, claim=True, no_error=False): def usb_connect(cls, serial, claim=True, no_error=False):
handle, usb_serial, bootstub, bcd = None, None, None, None handle, usb_serial, bootstub = None, None, None
context = usb1.USBContext() context = usb1.USBContext()
context.open() context.open()
try: try:
@@ -308,11 +290,6 @@ class Panda:
handle.claimInterface(0) handle.claimInterface(0)
# handle.setInterfaceAltSetting(0, 0) # Issue in USB stack # handle.setInterfaceAltSetting(0, 0) # Issue in USB stack
# bcdDevice wasn't always set to the hw type, ignore if it's the old constant
this_bcd = device.getbcdDevice()
if this_bcd is not None and this_bcd != 0x2300:
bcd = bytearray([this_bcd >> 8, ])
break break
except Exception: except Exception:
logger.exception("USB connect error") logger.exception("USB connect error")
@@ -323,7 +300,7 @@ class Panda:
else: else:
context.close() context.close()
return context, usb_handle, usb_serial, bootstub, bcd return context, usb_handle, usb_serial, bootstub
def is_connected_spi(self): def is_connected_spi(self):
return isinstance(self._handle, PandaSpiHandle) return isinstance(self._handle, PandaSpiHandle)
@@ -359,7 +336,7 @@ class Panda:
@classmethod @classmethod
def spi_list(cls): def spi_list(cls):
_, _, serial, _, _ = cls.spi_connect(None, ignore_version=True) _, _, serial, _ = cls.spi_connect(None, ignore_version=True)
if serial is not None: if serial is not None:
return [serial, ] return [serial, ]
return [] return []
@@ -731,7 +708,7 @@ class Panda:
@ensure_can_packet_version @ensure_can_packet_version
def can_send_many(self, arr, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS): def can_send_many(self, arr, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS):
snds = pack_can_buffer(arr, fd=fd) snds = pack_can_buffer(arr, chunk=(not self.spi), fd=fd)
for tx in snds: for tx in snds:
while len(tx) > 0: while len(tx) > 0:
bs = self._handle.bulkWrite(3, tx, timeout=timeout) bs = self._handle.bulkWrite(3, tx, timeout=timeout)

View File

@@ -1,5 +1,4 @@
import binascii import binascii
import ctypes
import os import os
import fcntl import fcntl
import math import math
@@ -8,7 +7,6 @@ import struct
import threading import threading
from contextlib import contextmanager from contextlib import contextmanager
from functools import reduce from functools import reduce
from collections.abc import Callable
from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT
from .constants import McuType, MCU_TYPE_BY_IDCODE, USBPACKET_MAX_SIZE from .constants import McuType, MCU_TYPE_BY_IDCODE, USBPACKET_MAX_SIZE
@@ -18,6 +16,10 @@ try:
import spidev import spidev
except ImportError: except ImportError:
spidev = None spidev = None
try:
import spidev2
except ImportError:
spidev2 = None
# Constants # Constants
SYNC = 0x5A SYNC = 0x5A
@@ -29,7 +31,8 @@ CHECKSUM_START = 0xAB
MIN_ACK_TIMEOUT_MS = 100 MIN_ACK_TIMEOUT_MS = 100
MAX_XFER_RETRY_COUNT = 5 MAX_XFER_RETRY_COUNT = 5
XFER_SIZE = 0x40*31 SPI_BUF_SIZE = 4096 # from panda/board/drivers/spi.h
XFER_SIZE = SPI_BUF_SIZE - 0x40 # give some room for SPI protocol overhead
DEV_PATH = "/dev/spidev0.0" DEV_PATH = "/dev/spidev0.0"
@@ -70,18 +73,6 @@ class PandaSpiTransferFailed(PandaSpiException):
pass pass
class PandaSpiTransfer(ctypes.Structure):
_fields_ = [
('rx_buf', ctypes.c_uint64),
('tx_buf', ctypes.c_uint64),
('tx_length', ctypes.c_uint32),
('rx_length_max', ctypes.c_uint32),
('timeout', ctypes.c_uint32),
('endpoint', ctypes.c_uint8),
('expect_disconnect', ctypes.c_uint8),
]
SPI_LOCK = threading.Lock() SPI_LOCK = threading.Lock()
SPI_DEVICES = {} SPI_DEVICES = {}
class SpiDevice: class SpiDevice:
@@ -89,9 +80,7 @@ class SpiDevice:
Provides locked, thread-safe access to a panda's SPI interface. Provides locked, thread-safe access to a panda's SPI interface.
""" """
# 50MHz is the max of the 845. older rev comma three MAX_SPEED = 50000000 # max of the SDM845
# may not support the full 50MHz
MAX_SPEED = 50000000
def __init__(self, speed=MAX_SPEED): def __init__(self, speed=MAX_SPEED):
assert speed <= self.MAX_SPEED assert speed <= self.MAX_SPEED
@@ -128,24 +117,12 @@ class PandaSpiHandle(BaseHandle):
""" """
PROTOCOL_VERSION = 2 PROTOCOL_VERSION = 2
HEADER = struct.Struct("<BBHH")
def __init__(self) -> None: def __init__(self) -> None:
self.dev = SpiDevice() self.dev = SpiDevice()
if spidev2 is not None and "SPI2" in os.environ:
self._transfer_raw: Callable[[SpiDevice, int, bytes, int, int, bool], bytes] = self._transfer_spidev self._spi2 = spidev2.SPIBus("/dev/spidev0.0", "w+b", bits_per_word=8, speed_hz=50_000_000)
if "KERN" in os.environ:
self._transfer_raw = self._transfer_kernel_driver
self.tx_buf = bytearray(1024)
self.rx_buf = bytearray(1024)
tx_buf_raw = ctypes.c_char.from_buffer(self.tx_buf)
rx_buf_raw = ctypes.c_char.from_buffer(self.rx_buf)
self.ioctl_data = PandaSpiTransfer()
self.ioctl_data.tx_buf = ctypes.addressof(tx_buf_raw)
self.ioctl_data.rx_buf = ctypes.addressof(rx_buf_raw)
self.fileno = self.dev._spidev.fileno()
# helpers # helpers
def _calc_checksum(self, data: bytes) -> int: def _calc_checksum(self, data: bytes) -> int:
@@ -160,10 +137,10 @@ class PandaSpiHandle(BaseHandle):
start = time.monotonic() start = time.monotonic()
while (timeout == 0) or ((time.monotonic() - start) < timeout_s): while (timeout == 0) or ((time.monotonic() - start) < timeout_s):
dat = spi.xfer2([tx, ] * length) dat = spi.xfer2([tx, ] * length)
if dat[0] == NACK: if dat[0] == ack_val:
raise PandaSpiNackResponse
elif dat[0] == ack_val:
return bytes(dat) return bytes(dat)
elif dat[0] == NACK:
raise PandaSpiNackResponse
raise PandaSpiMissingAck raise PandaSpiMissingAck
@@ -171,7 +148,7 @@ class PandaSpiHandle(BaseHandle):
max_rx_len = max(USBPACKET_MAX_SIZE, max_rx_len) max_rx_len = max(USBPACKET_MAX_SIZE, max_rx_len)
logger.debug("- send header") logger.debug("- send header")
packet = struct.pack("<BBHH", SYNC, endpoint, len(data), max_rx_len) packet = self.HEADER.pack(SYNC, endpoint, len(data), max_rx_len)
packet += bytes([self._calc_checksum(packet), ]) packet += bytes([self._calc_checksum(packet), ])
spi.xfer2(packet) spi.xfer2(packet)
@@ -200,29 +177,57 @@ class PandaSpiHandle(BaseHandle):
if remaining > 0: if remaining > 0:
dat += bytes(spi.readbytes(remaining)) dat += bytes(spi.readbytes(remaining))
dat = dat[:3 + response_len + 1] dat = dat[:3 + response_len + 1]
if self._calc_checksum(dat) != 0: if self._calc_checksum(dat) != 0:
raise PandaSpiBadChecksum raise PandaSpiBadChecksum
return dat[3:-1] return dat[3:-1]
def _transfer_kernel_driver(self, spi, endpoint: int, data, timeout: int, max_rx_len: int = 1000, expect_disconnect: bool = False) -> bytes: def _transfer_spidev2(self, spi, endpoint: int, data, timeout: int, max_rx_len: int = USBPACKET_MAX_SIZE, expect_disconnect: bool = False) -> bytes:
import spidev2 max_rx_len = max(USBPACKET_MAX_SIZE, max_rx_len)
self.tx_buf[:len(data)] = data
self.ioctl_data.endpoint = endpoint
self.ioctl_data.tx_length = len(data)
self.ioctl_data.rx_length_max = max_rx_len
self.ioctl_data.expect_disconnect = int(expect_disconnect)
# TODO: use our own ioctl request header = self.HEADER.pack(SYNC, endpoint, len(data), max_rx_len)
try:
ret = fcntl.ioctl(self.fileno, spidev2.SPI_IOC_RD_LSB_FIRST, self.ioctl_data) header_ack = bytearray(1)
except OSError as e:
raise PandaSpiException from e # ACK + <2 bytes for response length> + data + checksum
if ret < 0: data_rx = bytearray(3+max_rx_len+1)
raise PandaSpiException(f"ioctl returned {ret}")
return bytes(self.rx_buf[:ret]) self._spi2.submitTransferList(spidev2.SPITransferList((
# header
{'tx_buf': header + bytes([self._calc_checksum(header), ]), 'delay_usecs': 0, 'cs_change': True},
{'rx_buf': header_ack, 'delay_usecs': 0, 'cs_change': True},
# send data
{'tx_buf': bytes([*data, self._calc_checksum(data)]), 'delay_usecs': 0, 'cs_change': True},
{'rx_buf': data_rx, 'delay_usecs': 0, 'cs_change': True},
)))
if header_ack[0] != HACK:
raise PandaSpiMissingAck
if expect_disconnect:
logger.debug("- expecting disconnect, returning")
return b""
else:
dat = bytes(data_rx)
if dat[0] != DACK:
if dat[0] == NACK:
raise PandaSpiNackResponse
print("trying again")
dat = self._wait_for_ack(spi, DACK, timeout, 0x13, length=3 + max_rx_len)
# get response length, then response
response_len = struct.unpack("<H", dat[1:3])[0]
if response_len > max_rx_len:
raise PandaSpiException(f"response length greater than max ({max_rx_len} {response_len})")
dat = dat[:3 + response_len + 1]
if self._calc_checksum(dat) != 0:
raise PandaSpiBadChecksum
return dat[3:-1]
def _transfer(self, endpoint: int, data, timeout: int, max_rx_len: int = 1000, expect_disconnect: bool = False) -> bytes: def _transfer(self, endpoint: int, data, timeout: int, max_rx_len: int = 1000, expect_disconnect: bool = False) -> bytes:
logger.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len) logger.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
@@ -236,11 +241,25 @@ class PandaSpiHandle(BaseHandle):
logger.debug("\ntry #%d", n) logger.debug("\ntry #%d", n)
with self.dev.acquire() as spi: with self.dev.acquire() as spi:
try: try:
return self._transfer_raw(spi, endpoint, data, timeout, max_rx_len, expect_disconnect) fn = self._transfer_spidev
#fn = self._transfer_spidev2
return fn(spi, endpoint, data, timeout, max_rx_len, expect_disconnect)
except PandaSpiException as e: except PandaSpiException as e:
exc = e exc = e
logger.debug("SPI transfer failed, retrying", exc_info=True) logger.debug("SPI transfer failed, retrying", exc_info=True)
# ensure slave is in a consistent state and ready for the next transfer
# (e.g. slave TX buffer isn't stuck full)
nack_cnt = 0
attempts = 5
while (nack_cnt <= 3) and (attempts > 0):
attempts -= 1
try:
self._wait_for_ack(spi, NACK, MIN_ACK_TIMEOUT_MS, 0x11, length=XFER_SIZE//2)
nack_cnt += 1
except PandaSpiException:
nack_cnt = 0
raise exc raise exc
def get_protocol_version(self) -> bytes: def get_protocol_version(self) -> bytes:
@@ -290,8 +309,9 @@ class PandaSpiHandle(BaseHandle):
return self._transfer(0, struct.pack("<BHHH", request, value, index, length), timeout, max_rx_len=length) return self._transfer(0, struct.pack("<BHHH", request, value, index, length), timeout, max_rx_len=length)
def bulkWrite(self, endpoint: int, data: bytes, timeout: int = TIMEOUT) -> int: def bulkWrite(self, endpoint: int, data: bytes, timeout: int = TIMEOUT) -> int:
mv = memoryview(data)
for x in range(math.ceil(len(data) / XFER_SIZE)): for x in range(math.ceil(len(data) / XFER_SIZE)):
self._transfer(endpoint, data[XFER_SIZE*x:XFER_SIZE*(x+1)], timeout) self._transfer(endpoint, mv[XFER_SIZE*x:XFER_SIZE*(x+1)], timeout)
return len(data) return len(data)
def bulkRead(self, endpoint: int, length: int, timeout: int = TIMEOUT) -> bytes: def bulkRead(self, endpoint: int, length: int, timeout: int = TIMEOUT) -> bytes:
@@ -308,6 +328,9 @@ class STBootloaderSPIHandle(BaseSTBootloaderHandle):
""" """
Implementation of the STM32 SPI bootloader protocol described in: Implementation of the STM32 SPI bootloader protocol described in:
https://www.st.com/resource/en/application_note/an4286-spi-protocol-used-in-the-stm32-bootloader-stmicroelectronics.pdf https://www.st.com/resource/en/application_note/an4286-spi-protocol-used-in-the-stm32-bootloader-stmicroelectronics.pdf
NOTE: the bootloader's state machine is fragile and immediately gets into a bad state when
sending any junk, e.g. when using the panda SPI protocol.
""" """
SYNC = 0x5A SYNC = 0x5A

View File

@@ -1,17 +1,32 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import io
import os
import time import time
import pstats
import cProfile
from contextlib import contextmanager from contextlib import contextmanager
from panda import Panda, PandaDFU from panda import Panda, PandaDFU
from panda.tests.hitl.helpers import get_random_can_messages from panda.tests.hitl.helpers import get_random_can_messages
PROFILE = "PROFILE" in os.environ
@contextmanager @contextmanager
def print_time(desc): def print_time(desc):
if PROFILE:
pr = cProfile.Profile()
pr.enable()
start = time.perf_counter() start = time.perf_counter()
yield yield
end = time.perf_counter() end = time.perf_counter()
print(f"{end - start:.2f}s - {desc}") print(f"{end - start:.3f}s - {desc}")
if PROFILE:
pr.disable()
s = io.StringIO()
ps = pstats.Stats(pr, stream=s).sort_stats("cumtime")
ps.print_stats()
print(s.getvalue())
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -38,5 +38,5 @@ if ! command -v uv &>/dev/null; then
fi fi
export UV_PROJECT_ENVIRONMENT="$DIR/.venv" export UV_PROJECT_ENVIRONMENT="$DIR/.venv"
uv sync --all-extras uv sync --all-extras --upgrade
source "$DIR/.venv/bin/activate" source "$DIR/.venv/bin/activate"

View File

@@ -3,31 +3,9 @@ import pytest
import random import random
from unittest.mock import patch from unittest.mock import patch
from panda import Panda, PandaDFU from panda import Panda
from panda.python.spi import SpiDevice, PandaProtocolMismatch, PandaSpiNackResponse from panda.python.spi import PandaProtocolMismatch, PandaSpiNackResponse
pytestmark = [
pytest.mark.test_panda_types((Panda.HW_TYPE_TRES, ))
]
@pytest.mark.skip("doesn't work, bootloader seems to ignore commands once it sees junk")
def test_dfu_with_spam(p):
dfu_serial = p.get_dfu_serial()
# enter DFU
p.reset(enter_bootstub=True)
p.reset(enter_bootloader=True)
assert Panda.wait_for_dfu(dfu_serial, timeout=19), "failed to enter DFU"
# send junk
d = SpiDevice()
for _ in range(9):
with d.acquire() as spi:
dat = [random.randint(-1, 255) for _ in range(random.randint(1, 100))]
spi.xfer(dat)
# should still show up
assert dfu_serial in PandaDFU.list()
class TestSpi: class TestSpi:
def _ping(self, mocker, panda): def _ping(self, mocker, panda):

View File

@@ -4,18 +4,20 @@ import pytest
from panda import Panda from panda import Panda
pytestmark = [ pytestmark = [
pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES) pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES),
pytest.mark.test_panda_types([Panda.HW_TYPE_TRES])
] ]
@pytest.mark.timeout(2*60) @pytest.mark.timeout(2*60)
def test_fan_controller(p): def test_fan_curve(p):
# ensure fan curve is (roughly) linear
for power in (30, 50, 80, 100): for power in (30, 50, 80, 100):
p.set_fan_power(0) p.set_fan_power(0)
while p.get_fan_rpm() > 0: while p.get_fan_rpm() > 0:
time.sleep(0.1) time.sleep(0.1)
# wait until fan spins up (and recovers if needed), # wait until fan spins up, then wait a bit more for the RPM to converge
# then wait a bit more for the RPM to converge
p.set_fan_power(power) p.set_fan_power(power)
for _ in range(20): for _ in range(20):
time.sleep(1) time.sleep(1)
@@ -24,7 +26,7 @@ def test_fan_controller(p):
time.sleep(5) time.sleep(5)
expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100
assert 0.9 * expected_rpm <= p.get_fan_rpm() <= 1.1 * expected_rpm assert 0.75 * expected_rpm <= p.get_fan_rpm() <= 1.25 * expected_rpm
def test_fan_cooldown(p): def test_fan_cooldown(p):
# if the fan cooldown doesn't work, we get high frequency noise on the tach line # if the fan cooldown doesn't work, we get high frequency noise on the tach line
@@ -35,21 +37,3 @@ def test_fan_cooldown(p):
for _ in range(5): for _ in range(5):
assert p.get_fan_rpm() <= Panda.MAX_FAN_RPMs[bytes(p.get_type())] assert p.get_fan_rpm() <= Panda.MAX_FAN_RPMs[bytes(p.get_type())]
time.sleep(0.5) time.sleep(0.5)
def test_fan_overshoot(p):
# make sure it's stopped completely
p.set_fan_power(0)
while p.get_fan_rpm() > 0:
time.sleep(0.1)
# set it to 30% power to mimic going onroad
p.set_fan_power(30)
max_rpm = 0
for _ in range(50):
max_rpm = max(max_rpm, p.get_fan_rpm())
time.sleep(0.1)
# tolerate 10% overshoot
expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * 30 / 100
assert max_rpm <= 1.1 * expected_rpm, f"Fan overshoot: {(max_rpm / expected_rpm * 100) - 100:.1f}%"

View File

@@ -21,7 +21,7 @@ env = Environment(
'-Wfatal-errors', '-Wfatal-errors',
'-Wno-pointer-to-int-cast', '-Wno-pointer-to-int-cast',
], ],
CPPPATH=[".", "../../board/", opendbc.INCLUDE_PATH], CPPPATH=[".", "../../", "../../board/", opendbc.INCLUDE_PATH],
) )
if system == "Darwin": if system == "Darwin":
env.PrependENVPath('PATH', '/opt/homebrew/bin') env.PrependENVPath('PATH', '/opt/homebrew/bin')

View File

@@ -53,7 +53,7 @@ patterns = [
all_files = glob.glob('board/**', root_dir=ROOT, recursive=True) all_files = glob.glob('board/**', root_dir=ROOT, recursive=True)
files = [f for f in all_files if f.endswith(('.c', '.h')) and not f.startswith(IGNORED_PATHS)] files = [f for f in all_files if f.endswith(('.c', '.h')) and not f.startswith(IGNORED_PATHS)]
assert len(files) > 70, all(d in files for d in ('board/main.c', 'board/stm32h7/llfdcan.h')) assert len(files) > 50, all(d in files for d in ('board/main.c', 'board/stm32h7/llfdcan.h'))
for p in patterns: for p in patterns:
mutations.append((random.choice(files), p, True)) mutations.append((random.choice(files), p, True))

View File

@@ -78,17 +78,17 @@ class TestPandaComms(unittest.TestCase):
def test_comms_reset_tx(self): def test_comms_reset_tx(self):
# store some test messages in the queue # store some test messages in the queue
test_msg = (0x100, b"test", 0) test_msg = (0x100, b"test", 0)
packed = pack_can_buffer([test_msg for _ in range(100)]) packed = pack_can_buffer([test_msg for _ in range(100)], chunk=True)
# write a small chunk such that we have some overflow # write a small chunk such that we have some overflow
TINY_CHUNK_SIZE = 6 TINY_CHUNK_SIZE = 6
lpp.comms_can_write(packed[0][:TINY_CHUNK_SIZE], TINY_CHUNK_SIZE) lpp.comms_can_write(bytes(packed[0][:TINY_CHUNK_SIZE]), TINY_CHUNK_SIZE)
# reset the comms to clear the overflow buffer on the panda side # reset the comms to clear the overflow buffer on the panda side
lpp.comms_can_reset() lpp.comms_can_reset()
# write a full valid chunk, which should now contain valid messages # write a full valid chunk, which should now contain valid messages
lpp.comms_can_write(packed[1], len(packed[1])) lpp.comms_can_write(bytes(packed[1]), len(packed[1]))
# read the messages from the queue and make sure they're valid # read the messages from the queue and make sure they're valid
queue_msgs = [] queue_msgs = []
@@ -114,7 +114,7 @@ class TestPandaComms(unittest.TestCase):
for buf in packed: for buf in packed:
for i in range(0, len(buf), CHUNK_SIZE): for i in range(0, len(buf), CHUNK_SIZE):
chunk_len = min(CHUNK_SIZE, len(buf) - i) chunk_len = min(CHUNK_SIZE, len(buf) - i)
lpp.comms_can_write(buf[i:i+chunk_len], chunk_len) lpp.comms_can_write(bytes(buf[i:i+chunk_len]), chunk_len)
# Check that they ended up in the right buffers # Check that they ended up in the right buffers
queue_msgs = [] queue_msgs = []