commit
0c606ee339
105 changed files with 2448 additions and 1677 deletions
@ -1 +1 @@ |
|||||||
v1.5.9 |
v1.6.9 |
@ -0,0 +1,23 @@ |
|||||||
|
// ********************* Critical section helpers *********************
|
||||||
|
volatile bool interrupts_enabled = false; |
||||||
|
|
||||||
|
void enable_interrupts(void) { |
||||||
|
interrupts_enabled = true; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void disable_interrupts(void) { |
||||||
|
interrupts_enabled = false; |
||||||
|
__disable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t global_critical_depth = 0U; |
||||||
|
#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(); \
|
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
typedef struct interrupt { |
||||||
|
IRQn_Type irq_type; |
||||||
|
void (*handler)(void); |
||||||
|
uint32_t call_counter; |
||||||
|
uint32_t max_call_rate; // Call rate is defined as the amount of calls each second
|
||||||
|
uint32_t call_rate_fault; |
||||||
|
} interrupt; |
||||||
|
|
||||||
|
void unused_interrupt_handler(void) { |
||||||
|
// Something is wrong if this handler is called!
|
||||||
|
puts("Unused interrupt handler called!\n"); |
||||||
|
fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); |
||||||
|
} |
||||||
|
|
||||||
|
#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h)
|
||||||
|
interrupt interrupts[NUM_INTERRUPTS]; |
||||||
|
|
||||||
|
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate, rate_fault) \ |
||||||
|
interrupts[irq_num].irq_type = irq_num; \
|
||||||
|
interrupts[irq_num].handler = func_ptr; \
|
||||||
|
interrupts[irq_num].call_counter = 0U; \
|
||||||
|
interrupts[irq_num].max_call_rate = call_rate; \
|
||||||
|
interrupts[irq_num].call_rate_fault = rate_fault; |
||||||
|
|
||||||
|
bool check_interrupt_rate = false; |
||||||
|
|
||||||
|
void handle_interrupt(IRQn_Type irq_type){ |
||||||
|
interrupts[irq_type].call_counter++; |
||||||
|
interrupts[irq_type].handler(); |
||||||
|
|
||||||
|
// Check that the interrupts don't fire too often
|
||||||
|
if(check_interrupt_rate && (interrupts[irq_type].call_counter > interrupts[irq_type].max_call_rate)){ |
||||||
|
puts("Interrupt 0x"); puth(irq_type); puts(" fired too often (0x"); puth(interrupts[irq_type].call_counter); puts("/s)!\n"); |
||||||
|
fault_occurred(interrupts[irq_type].call_rate_fault); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Reset interrupt counter every second
|
||||||
|
void TIM6_DAC_IRQ_Handler(void) { |
||||||
|
if (TIM6->SR != 0) { |
||||||
|
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){ |
||||||
|
interrupts[i].call_counter = 0U; |
||||||
|
} |
||||||
|
} |
||||||
|
TIM6->SR = 0; |
||||||
|
} |
||||||
|
|
||||||
|
void init_interrupts(bool check_rate_limit){ |
||||||
|
check_interrupt_rate = check_rate_limit; |
||||||
|
|
||||||
|
for(uint16_t i=0U; i<NUM_INTERRUPTS; i++){ |
||||||
|
interrupts[i].handler = unused_interrupt_handler; |
||||||
|
} |
||||||
|
|
||||||
|
// Init timer 10 for a 1s interval
|
||||||
|
register_set_bits(&(RCC->APB1ENR), RCC_APB1ENR_TIM6EN); // Enable interrupt timer peripheral
|
||||||
|
REGISTER_INTERRUPT(TIM6_DAC_IRQn, TIM6_DAC_IRQ_Handler, 1, FAULT_INTERRUPT_RATE_INTERRUPTS) |
||||||
|
register_set(&(TIM6->PSC), (732-1), 0xFFFFU); |
||||||
|
register_set(&(TIM6->DIER), TIM_DIER_UIE, 0x5F5FU); |
||||||
|
register_set(&(TIM6->CR1), TIM_CR1_CEN, 0x3FU); |
||||||
|
TIM6->SR = 0; |
||||||
|
NVIC_EnableIRQ(TIM6_DAC_IRQn); |
||||||
|
} |
||||||
|
|
||||||
|
// ********************* Bare interrupt handlers *********************
|
||||||
|
// Only implemented the STM32F413 interrupts for now, the STM32F203 specific ones do not fall into the scope of SIL2
|
||||||
|
|
||||||
|
void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} |
||||||
|
void PVD_IRQHandler(void) {handle_interrupt(PVD_IRQn);} |
||||||
|
void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);} |
||||||
|
void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);} |
||||||
|
void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);} |
||||||
|
void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);} |
||||||
|
void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);} |
||||||
|
void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);} |
||||||
|
void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);} |
||||||
|
void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);} |
||||||
|
void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);} |
||||||
|
void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);} |
||||||
|
void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);} |
||||||
|
void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);} |
||||||
|
void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);} |
||||||
|
void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);} |
||||||
|
void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);} |
||||||
|
void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);} |
||||||
|
void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);} |
||||||
|
void CAN1_TX_IRQHandler(void) {handle_interrupt(CAN1_TX_IRQn);} |
||||||
|
void CAN1_RX0_IRQHandler(void) {handle_interrupt(CAN1_RX0_IRQn);} |
||||||
|
void CAN1_RX1_IRQHandler(void) {handle_interrupt(CAN1_RX1_IRQn);} |
||||||
|
void CAN1_SCE_IRQHandler(void) {handle_interrupt(CAN1_SCE_IRQn);} |
||||||
|
void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} |
||||||
|
void TIM1_BRK_TIM9_IRQHandler(void) {handle_interrupt(TIM1_BRK_TIM9_IRQn);} |
||||||
|
void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} |
||||||
|
void TIM1_TRG_COM_TIM11_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_TIM11_IRQn);} |
||||||
|
void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);} |
||||||
|
void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);} |
||||||
|
void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);} |
||||||
|
void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);} |
||||||
|
void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);} |
||||||
|
void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);} |
||||||
|
void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);} |
||||||
|
void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);} |
||||||
|
void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);} |
||||||
|
void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);} |
||||||
|
void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);} |
||||||
|
void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);} |
||||||
|
void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);} |
||||||
|
void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);} |
||||||
|
void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);} |
||||||
|
void OTG_FS_WKUP_IRQHandler(void) {handle_interrupt(OTG_FS_WKUP_IRQn);} |
||||||
|
void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);} |
||||||
|
void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);} |
||||||
|
void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);} |
||||||
|
void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);} |
||||||
|
void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);} |
||||||
|
void FSMC_IRQHandler(void) {handle_interrupt(FSMC_IRQn);} |
||||||
|
void SDIO_IRQHandler(void) {handle_interrupt(SDIO_IRQn);} |
||||||
|
void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} |
||||||
|
void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} |
||||||
|
void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);} |
||||||
|
void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);} |
||||||
|
void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);} |
||||||
|
void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);} |
||||||
|
void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);} |
||||||
|
void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);} |
||||||
|
void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);} |
||||||
|
void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);} |
||||||
|
void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);} |
||||||
|
void CAN2_TX_IRQHandler(void) {handle_interrupt(CAN2_TX_IRQn);} |
||||||
|
void CAN2_RX0_IRQHandler(void) {handle_interrupt(CAN2_RX0_IRQn);} |
||||||
|
void CAN2_RX1_IRQHandler(void) {handle_interrupt(CAN2_RX1_IRQn);} |
||||||
|
void CAN2_SCE_IRQHandler(void) {handle_interrupt(CAN2_SCE_IRQn);} |
||||||
|
void OTG_FS_IRQHandler(void) {handle_interrupt(OTG_FS_IRQn);} |
||||||
|
void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);} |
||||||
|
void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);} |
||||||
|
void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);} |
||||||
|
void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);} |
||||||
|
void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);} |
||||||
|
void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);} |
||||||
|
#ifdef STM32F4 |
||||||
|
void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);} |
||||||
|
void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);} |
||||||
|
void CAN3_TX_IRQHandler(void) {handle_interrupt(CAN3_TX_IRQn);} |
||||||
|
void CAN3_RX0_IRQHandler(void) {handle_interrupt(CAN3_RX0_IRQn);} |
||||||
|
void CAN3_RX1_IRQHandler(void) {handle_interrupt(CAN3_RX1_IRQn);} |
||||||
|
void CAN3_SCE_IRQHandler(void) {handle_interrupt(CAN3_SCE_IRQn);} |
||||||
|
void RNG_IRQHandler(void) {handle_interrupt(RNG_IRQn);} |
||||||
|
void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);} |
||||||
|
void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} |
||||||
|
void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);} |
||||||
|
void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);} |
||||||
|
void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);} |
||||||
|
void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);} |
||||||
|
void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);} |
||||||
|
void UART10_IRQHandler(void) {handle_interrupt(UART10_IRQn);} |
||||||
|
void QUADSPI_IRQHandler(void) {handle_interrupt(QUADSPI_IRQn);} |
||||||
|
void FMPI2C1_EV_IRQHandler(void) {handle_interrupt(FMPI2C1_EV_IRQn);} |
||||||
|
void FMPI2C1_ER_IRQHandler(void) {handle_interrupt(FMPI2C1_ER_IRQn);} |
||||||
|
void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);} |
||||||
|
void DFSDM2_FLT0_IRQHandler(void) {handle_interrupt(DFSDM2_FLT0_IRQn);} |
||||||
|
void DFSDM2_FLT1_IRQHandler(void) {handle_interrupt(DFSDM2_FLT1_IRQn);} |
||||||
|
void DFSDM2_FLT2_IRQHandler(void) {handle_interrupt(DFSDM2_FLT2_IRQn);} |
||||||
|
void DFSDM2_FLT3_IRQHandler(void) {handle_interrupt(DFSDM2_FLT3_IRQn);} |
||||||
|
#endif |
@ -0,0 +1,81 @@ |
|||||||
|
|
||||||
|
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)) |
||||||
|
|
||||||
|
reg register_map[REGISTER_MAP_SIZE]; |
||||||
|
|
||||||
|
// Hash spread in first and second iterations seems to be reasonable.
|
||||||
|
// See: tests/development/register_hashmap_spread.py
|
||||||
|
// Also, check the collision warnings in the debug output, and minimize those.
|
||||||
|
uint16_t hash_addr(uint32_t input){ |
||||||
|
return (((input >> 16U) ^ ((((input + 1U) & 0xFFFFU) * HASHING_PRIME) & 0xFFFFU)) & REGISTER_MAP_SIZE); |
||||||
|
} |
||||||
|
|
||||||
|
// 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){ |
||||||
|
ENTER_CRITICAL() |
||||||
|
// Set bits in register that are also in the mask
|
||||||
|
(*addr) = ((*addr) & (~mask)) | (val & mask); |
||||||
|
|
||||||
|
// Add these values to the map
|
||||||
|
uint16_t hash = hash_addr((uint32_t) addr); |
||||||
|
uint16_t tries = REGISTER_MAP_SIZE; |
||||||
|
while(CHECK_COLLISION(hash, addr) && (tries > 0U)) { hash = hash_addr((uint32_t) hash); tries--;} |
||||||
|
if (tries != 0U){ |
||||||
|
register_map[hash].address = addr; |
||||||
|
register_map[hash].value = (register_map[hash].value & (~mask)) | (val & mask); |
||||||
|
register_map[hash].check_mask |= mask; |
||||||
|
} else { |
||||||
|
#ifdef DEBUG_FAULTS |
||||||
|
puts("Hash collision: address 0x"); puth((uint32_t) addr); puts("!\n"); |
||||||
|
#endif |
||||||
|
} |
||||||
|
EXIT_CRITICAL() |
||||||
|
} |
||||||
|
|
||||||
|
// 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) { |
||||||
|
return register_set(addr, val, 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) { |
||||||
|
return register_set(addr, (~val), val); |
||||||
|
} |
||||||
|
|
||||||
|
// To be called periodically
|
||||||
|
void check_registers(void){ |
||||||
|
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){ |
||||||
|
if((uint32_t) register_map[i].address != 0U){ |
||||||
|
ENTER_CRITICAL() |
||||||
|
if((*(register_map[i].address) & register_map[i].check_mask) != (register_map[i].value & register_map[i].check_mask)){ |
||||||
|
#ifdef DEBUG_FAULTS |
||||||
|
puts("Register at address 0x"); puth((uint32_t) register_map[i].address); puts(" is divergent!"); |
||||||
|
puts(" Map: 0x"); puth(register_map[i].value); |
||||||
|
puts(" Register: 0x"); puth(*(register_map[i].address)); |
||||||
|
puts(" Mask: 0x"); puth(register_map[i].check_mask); |
||||||
|
puts("\n"); |
||||||
|
#endif |
||||||
|
fault_occurred(FAULT_REGISTER_DIVERGENT); |
||||||
|
} |
||||||
|
EXIT_CRITICAL() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void init_registers(void) { |
||||||
|
for(uint16_t i=0U; i<REGISTER_MAP_SIZE; i++){ |
||||||
|
register_map[i].address = (volatile uint32_t *) 0U; |
||||||
|
register_map[i].check_mask = 0U; |
||||||
|
} |
||||||
|
} |
@ -1,7 +1,7 @@ |
|||||||
void timer_init(TIM_TypeDef *TIM, int psc) { |
void timer_init(TIM_TypeDef *TIM, int psc) { |
||||||
TIM->PSC = psc-1; |
register_set(&(TIM->PSC), (psc-1), 0xFFFFU); |
||||||
TIM->DIER = TIM_DIER_UIE; |
register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); |
||||||
TIM->CR1 = TIM_CR1_CEN; |
register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); |
||||||
TIM->SR = 0; |
TIM->SR = 0; |
||||||
} |
} |
||||||
|
|
||||||
|
@ -0,0 +1,49 @@ |
|||||||
|
#define FAULT_STATUS_NONE 0U |
||||||
|
#define FAULT_STATUS_TEMPORARY 1U |
||||||
|
#define FAULT_STATUS_PERMANENT 2U |
||||||
|
|
||||||
|
// Fault types
|
||||||
|
#define FAULT_RELAY_MALFUNCTION (1U << 0) |
||||||
|
#define FAULT_UNUSED_INTERRUPT_HANDLED (1U << 1) |
||||||
|
#define FAULT_INTERRUPT_RATE_CAN_1 (1U << 2) |
||||||
|
#define FAULT_INTERRUPT_RATE_CAN_2 (1U << 3) |
||||||
|
#define FAULT_INTERRUPT_RATE_CAN_3 (1U << 4) |
||||||
|
#define FAULT_INTERRUPT_RATE_TACH (1U << 5) |
||||||
|
#define FAULT_INTERRUPT_RATE_GMLAN (1U << 6) |
||||||
|
#define FAULT_INTERRUPT_RATE_INTERRUPTS (1U << 7) |
||||||
|
#define FAULT_INTERRUPT_RATE_SPI_DMA (1U << 8) |
||||||
|
#define FAULT_INTERRUPT_RATE_SPI_CS (1U << 9) |
||||||
|
#define FAULT_INTERRUPT_RATE_UART_1 (1U << 10) |
||||||
|
#define FAULT_INTERRUPT_RATE_UART_2 (1U << 11) |
||||||
|
#define FAULT_INTERRUPT_RATE_UART_3 (1U << 12) |
||||||
|
#define FAULT_INTERRUPT_RATE_UART_5 (1U << 13) |
||||||
|
#define FAULT_INTERRUPT_RATE_UART_DMA (1U << 14) |
||||||
|
#define FAULT_INTERRUPT_RATE_USB (1U << 15) |
||||||
|
#define FAULT_INTERRUPT_RATE_TIM1 (1U << 16) |
||||||
|
#define FAULT_INTERRUPT_RATE_TIM3 (1U << 17) |
||||||
|
#define FAULT_REGISTER_DIVERGENT (1U << 18) |
||||||
|
|
||||||
|
// Permanent faults
|
||||||
|
#define PERMANENT_FAULTS 0U |
||||||
|
|
||||||
|
uint8_t fault_status = FAULT_STATUS_NONE; |
||||||
|
uint32_t faults = 0U; |
||||||
|
|
||||||
|
void fault_occurred(uint32_t fault) { |
||||||
|
faults |= fault; |
||||||
|
if((PERMANENT_FAULTS & fault) != 0U){ |
||||||
|
puts("Permanent fault occurred: 0x"); puth(fault); puts("\n"); |
||||||
|
fault_status = FAULT_STATUS_PERMANENT; |
||||||
|
} else { |
||||||
|
puts("Temporary fault occurred: 0x"); puth(fault); puts("\n"); |
||||||
|
fault_status = FAULT_STATUS_TEMPORARY; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void fault_recovered(uint32_t fault) { |
||||||
|
if((PERMANENT_FAULTS & fault) == 0U){ |
||||||
|
faults &= ~fault; |
||||||
|
} else { |
||||||
|
puts("Cannot recover from a permanent fault!\n"); |
||||||
|
} |
||||||
|
} |
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d0d192a64b7d859a76a631cf9bfdb865825ca83172a455eff661217c56392638 |
oid sha256:7c2cb1deecee360e4e8069c4215f05f652e14c70bd88f9bd072dc5502a5fd530 |
||||||
size 1145211 |
size 1145325 |
||||||
|
@ -1,59 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
from panda import Panda |
|
||||||
from panda.python.uds import UdsClient, NegativeResponseError, DATA_IDENTIFIER_TYPE |
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
address = 0x18da30f1 # Honda EPS |
|
||||||
panda = Panda() |
|
||||||
uds_client = UdsClient(panda, address, debug=False) |
|
||||||
|
|
||||||
print("tester present ...") |
|
||||||
uds_client.tester_present() |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: boot software id ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: application software id ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: application data id ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: boot software fingerprint ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: application software fingerprint ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
||||||
|
|
||||||
try: |
|
||||||
print("") |
|
||||||
print("read data by id: application data fingerprint ...") |
|
||||||
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT) |
|
||||||
print(data.decode('utf-8')) |
|
||||||
except NegativeResponseError as e: |
|
||||||
print(e) |
|
@ -0,0 +1,77 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
from tqdm import tqdm |
||||||
|
from panda import Panda |
||||||
|
from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, SESSION_TYPE, DATA_IDENTIFIER_TYPE |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
addrs = [0x700 + i for i in range(256)] |
||||||
|
addrs += [0x18da0000 + (i<<8) + 0xf1 for i in range(256)] |
||||||
|
results = {} |
||||||
|
|
||||||
|
panda = Panda() |
||||||
|
panda.set_safety_mode(Panda.SAFETY_ELM327) |
||||||
|
print("querying addresses ...") |
||||||
|
with tqdm(addrs) as t: |
||||||
|
for addr in t: |
||||||
|
# skip functional broadcast addrs |
||||||
|
if addr == 0x7df or addr == 0x18db33f1: |
||||||
|
continue |
||||||
|
t.set_description(hex(addr)) |
||||||
|
|
||||||
|
uds_client = UdsClient(panda, addr, bus=1 if panda.has_obd() else 0, timeout=0.1, debug=False) |
||||||
|
try: |
||||||
|
uds_client.tester_present() |
||||||
|
uds_client.diagnostic_session_control(SESSION_TYPE.EXTENDED_DIAGNOSTIC) |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
except MessageTimeoutError: |
||||||
|
continue |
||||||
|
|
||||||
|
resp = {} |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_IDENTIFICATION] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.BOOT_SOFTWARE_FINGERPRINT] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_FINGERPRINT] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
try: |
||||||
|
data = uds_client.read_data_by_identifier(DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT) |
||||||
|
if data: resp[DATA_IDENTIFIER_TYPE.APPLICATION_DATA_FINGERPRINT] = data |
||||||
|
except NegativeResponseError: |
||||||
|
pass |
||||||
|
|
||||||
|
if resp.keys(): |
||||||
|
results[addr] = resp |
||||||
|
|
||||||
|
print("results:") |
||||||
|
if len(results.items()): |
||||||
|
for addr, resp in results.items(): |
||||||
|
for rid, dat in resp.items(): |
||||||
|
print(hex(addr), hex(rid), dat.decode()) |
||||||
|
else: |
||||||
|
print("no fw versions found!") |
@ -1,11 +1,34 @@ |
|||||||
from .helpers import test_all_pandas, panda_connect_and_init |
import os |
||||||
|
|
||||||
|
from nose.tools import assert_equal |
||||||
|
|
||||||
|
from panda import Panda, BASEDIR |
||||||
|
from .helpers import reset_pandas, test_all_pandas, panda_connect_and_init |
||||||
|
|
||||||
|
|
||||||
|
# Reset the pandas before flashing them |
||||||
|
def aaaa_reset_before_tests(): |
||||||
|
reset_pandas() |
||||||
|
|
||||||
|
|
||||||
@test_all_pandas |
@test_all_pandas |
||||||
@panda_connect_and_init |
@panda_connect_and_init |
||||||
def test_recover(p): |
def test_recover(p): |
||||||
assert p.recover(timeout=30) |
assert p.recover(timeout=30) |
||||||
|
|
||||||
|
|
||||||
@test_all_pandas |
@test_all_pandas |
||||||
@panda_connect_and_init |
@panda_connect_and_init |
||||||
def test_flash(p): |
def test_flash(p): |
||||||
p.flash() |
p.flash() |
||||||
|
|
||||||
|
|
||||||
|
@test_all_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_get_signature(p): |
||||||
|
fn = os.path.join(BASEDIR, "board/obj/panda.bin") |
||||||
|
|
||||||
|
firmware_sig = Panda.get_signature_from_firmware(fn) |
||||||
|
panda_sig = p.get_signature() |
||||||
|
|
||||||
|
assert_equal(panda_sig, firmware_sig) |
||||||
|
@ -0,0 +1,44 @@ |
|||||||
|
import time |
||||||
|
from panda_jungle import PandaJungle # pylint: disable=import-error |
||||||
|
from .helpers import panda_jungle, reset_pandas, test_all_pandas, test_all_gen2_pandas, panda_connect_and_init |
||||||
|
|
||||||
|
# Reset the pandas before running tests |
||||||
|
def aaaa_reset_before_tests(): |
||||||
|
reset_pandas() |
||||||
|
|
||||||
|
@test_all_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_ignition(p): |
||||||
|
try: |
||||||
|
# Set harness orientation to #2, since the ignition line is on the wrong SBU bus :/ |
||||||
|
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_2) |
||||||
|
reset_pandas() |
||||||
|
p.reconnect() |
||||||
|
panda_jungle.set_ignition(False) |
||||||
|
time.sleep(2) |
||||||
|
assert p.health()['ignition_line'] == False |
||||||
|
panda_jungle.set_ignition(True) |
||||||
|
time.sleep(2) |
||||||
|
assert p.health()['ignition_line'] == True |
||||||
|
finally: |
||||||
|
panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1) |
||||||
|
|
||||||
|
@test_all_gen2_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_orientation_detection(p): |
||||||
|
seen_orientations = [] |
||||||
|
for i in range(3): |
||||||
|
panda_jungle.set_harness_orientation(i) |
||||||
|
reset_pandas() |
||||||
|
p.reconnect() |
||||||
|
detected_harness_orientation = p.health()['car_harness_status'] |
||||||
|
if (i == 0 and detected_harness_orientation != 0) or detected_harness_orientation in seen_orientations: |
||||||
|
assert False |
||||||
|
seen_orientations.append(detected_harness_orientation) |
||||||
|
|
||||||
|
|
||||||
|
@test_all_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_voltage(p): |
||||||
|
voltage = p.health()['voltage'] |
||||||
|
assert ((voltage > 10000) and (voltage < 14000)) |
@ -1,8 +1,12 @@ |
|||||||
import time |
import time |
||||||
from panda import Panda |
from panda import Panda |
||||||
from .helpers import connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init |
from .helpers import reset_pandas, connect_wifi, test_white, test_all_pandas, panda_type_to_serial, panda_connect_and_init |
||||||
import requests |
import requests |
||||||
|
|
||||||
|
# Reset the pandas before running tests |
||||||
|
def aaaa_reset_before_tests(): |
||||||
|
reset_pandas() |
||||||
|
|
||||||
@test_all_pandas |
@test_all_pandas |
||||||
@panda_connect_and_init |
@panda_connect_and_init |
||||||
def test_get_serial(p): |
def test_get_serial(p): |
@ -1,195 +0,0 @@ |
|||||||
|
|
||||||
import os |
|
||||||
import time |
|
||||||
import random |
|
||||||
from panda import Panda |
|
||||||
from nose.tools import assert_equal, assert_less, assert_greater |
|
||||||
from .helpers import time_many_sends, test_two_panda, test_two_black_panda, panda_type_to_serial, clear_can_buffers, panda_connect_and_init |
|
||||||
|
|
||||||
@test_two_panda |
|
||||||
@panda_type_to_serial |
|
||||||
@panda_connect_and_init |
|
||||||
def test_send_recv(p_send, p_recv): |
|
||||||
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
p_send.set_can_loopback(False) |
|
||||||
p_recv.set_can_loopback(False) |
|
||||||
|
|
||||||
assert not p_send.legacy |
|
||||||
assert not p_recv.legacy |
|
||||||
|
|
||||||
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2) |
|
||||||
time.sleep(0.05) |
|
||||||
p_recv.can_recv() |
|
||||||
p_send.can_recv() |
|
||||||
|
|
||||||
busses = [0,1,2] |
|
||||||
|
|
||||||
for bus in busses: |
|
||||||
for speed in [100, 250, 500, 750, 1000]: |
|
||||||
p_send.set_can_speed_kbps(bus, speed) |
|
||||||
p_recv.set_can_speed_kbps(bus, speed) |
|
||||||
time.sleep(0.05) |
|
||||||
|
|
||||||
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) |
|
||||||
|
|
||||||
saturation_pct = (comp_kbps/speed) * 100.0 |
|
||||||
assert_greater(saturation_pct, 80) |
|
||||||
assert_less(saturation_pct, 100) |
|
||||||
|
|
||||||
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) |
|
||||||
|
|
||||||
@test_two_panda |
|
||||||
@panda_type_to_serial |
|
||||||
@panda_connect_and_init |
|
||||||
def test_latency(p_send, p_recv): |
|
||||||
p_send.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
p_send.set_can_loopback(False) |
|
||||||
p_recv.set_can_loopback(False) |
|
||||||
|
|
||||||
assert not p_send.legacy |
|
||||||
assert not p_recv.legacy |
|
||||||
|
|
||||||
p_send.set_can_speed_kbps(0, 100) |
|
||||||
p_recv.set_can_speed_kbps(0, 100) |
|
||||||
time.sleep(0.05) |
|
||||||
|
|
||||||
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
|
||||||
time.sleep(0.05) |
|
||||||
p_recv.can_recv() |
|
||||||
p_send.can_recv() |
|
||||||
|
|
||||||
busses = [0,1,2] |
|
||||||
|
|
||||||
for bus in busses: |
|
||||||
for speed in [100, 250, 500, 750, 1000]: |
|
||||||
p_send.set_can_speed_kbps(bus, speed) |
|
||||||
p_recv.set_can_speed_kbps(bus, speed) |
|
||||||
time.sleep(0.1) |
|
||||||
|
|
||||||
#clear can buffers |
|
||||||
clear_can_buffers(p_send) |
|
||||||
clear_can_buffers(p_recv) |
|
||||||
|
|
||||||
latencies = [] |
|
||||||
comp_kbps_list = [] |
|
||||||
saturation_pcts = [] |
|
||||||
|
|
||||||
num_messages = 100 |
|
||||||
|
|
||||||
for i in range(num_messages): |
|
||||||
st = time.time() |
|
||||||
p_send.can_send(0x1ab, b"message", bus) |
|
||||||
r = [] |
|
||||||
while len(r) < 1 and (time.time() - st) < 5: |
|
||||||
r = p_recv.can_recv() |
|
||||||
et = time.time() |
|
||||||
r_echo = [] |
|
||||||
while len(r_echo) < 1 and (time.time() - st) < 10: |
|
||||||
r_echo = p_send.can_recv() |
|
||||||
|
|
||||||
if len(r) == 0 or len(r_echo) == 0: |
|
||||||
print("r: {}, r_echo: {}".format(r, r_echo)) |
|
||||||
|
|
||||||
assert_equal(len(r),1) |
|
||||||
assert_equal(len(r_echo),1) |
|
||||||
|
|
||||||
et = (et - st)*1000.0 |
|
||||||
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et |
|
||||||
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) |
|
||||||
|
|
||||||
assert_less(latency, 5.0) |
|
||||||
|
|
||||||
saturation_pct = (comp_kbps/speed) * 100.0 |
|
||||||
latencies.append(latency) |
|
||||||
comp_kbps_list.append(comp_kbps) |
|
||||||
saturation_pcts.append(saturation_pct) |
|
||||||
|
|
||||||
average_latency = sum(latencies)/num_messages |
|
||||||
assert_less(average_latency, 1.0) |
|
||||||
average_comp_kbps = sum(comp_kbps_list)/num_messages |
|
||||||
average_saturation_pct = sum(saturation_pcts)/num_messages |
|
||||||
|
|
||||||
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ |
|
||||||
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) |
|
||||||
|
|
||||||
@test_two_black_panda |
|
||||||
@panda_type_to_serial |
|
||||||
@panda_connect_and_init |
|
||||||
def test_black_loopback(panda0, panda1): |
|
||||||
# disable safety modes |
|
||||||
panda0.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
panda1.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
|
||||||
|
|
||||||
# disable loopback |
|
||||||
panda0.set_can_loopback(False) |
|
||||||
panda1.set_can_loopback(False) |
|
||||||
|
|
||||||
# clear stuff |
|
||||||
panda0.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
|
||||||
time.sleep(0.05) |
|
||||||
panda0.can_recv() |
|
||||||
panda1.can_recv() |
|
||||||
|
|
||||||
# test array (send bus, sender obd, reciever obd, expected busses) |
|
||||||
test_array = [ |
|
||||||
(0, False, False, [0]), |
|
||||||
(1, False, False, [1]), |
|
||||||
(2, False, False, [2]), |
|
||||||
(0, False, True, [0, 1]), |
|
||||||
(1, False, True, []), |
|
||||||
(2, False, True, [2]), |
|
||||||
(0, True, False, [0]), |
|
||||||
(1, True, False, [0]), |
|
||||||
(2, True, False, [2]), |
|
||||||
(0, True, True, [0, 1]), |
|
||||||
(1, True, True, [0, 1]), |
|
||||||
(2, True, True, [2]) |
|
||||||
] |
|
||||||
|
|
||||||
# test functions |
|
||||||
def get_test_string(): |
|
||||||
return b"test"+os.urandom(10) |
|
||||||
|
|
||||||
def _test_buses(send_panda, recv_panda, _test_array): |
|
||||||
for send_bus, send_obd, recv_obd, recv_buses in _test_array: |
|
||||||
print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd) |
|
||||||
|
|
||||||
# set OBD on pandas |
|
||||||
send_panda.set_gmlan(True if send_obd else None) |
|
||||||
recv_panda.set_gmlan(True if recv_obd else None) |
|
||||||
|
|
||||||
# clear buffers |
|
||||||
clear_can_buffers(send_panda) |
|
||||||
clear_can_buffers(recv_panda) |
|
||||||
|
|
||||||
# send the characters |
|
||||||
at = random.randint(1, 2000) |
|
||||||
st = get_test_string()[0:8] |
|
||||||
send_panda.can_send(at, st, send_bus) |
|
||||||
time.sleep(0.1) |
|
||||||
|
|
||||||
# check for receive |
|
||||||
_ = send_panda.can_recv() # cans echo |
|
||||||
cans_loop = recv_panda.can_recv() |
|
||||||
|
|
||||||
loop_buses = [] |
|
||||||
for loop in cans_loop: |
|
||||||
print(" Loop on bus", str(loop[3])) |
|
||||||
loop_buses.append(loop[3]) |
|
||||||
if len(cans_loop) == 0: |
|
||||||
print(" No loop") |
|
||||||
|
|
||||||
# test loop buses |
|
||||||
recv_buses.sort() |
|
||||||
loop_buses.sort() |
|
||||||
assert recv_buses == loop_buses |
|
||||||
print(" TEST PASSED") |
|
||||||
print("\n") |
|
||||||
|
|
||||||
# test both orientations |
|
||||||
print("***************** TESTING (0 --> 1) *****************") |
|
||||||
_test_buses(panda0, panda1, test_array) |
|
||||||
print("***************** TESTING (1 --> 0) *****************") |
|
||||||
_test_buses(panda1, panda0, test_array) |
|
@ -1,16 +1,23 @@ |
|||||||
|
|
||||||
import sys |
import sys |
||||||
import time |
import time |
||||||
from .helpers import time_many_sends, connect_wifi, test_white, panda_type_to_serial |
from .helpers import start_heartbeat_thread, reset_pandas, time_many_sends, connect_wifi, test_white, panda_type_to_serial |
||||||
from panda import Panda, PandaWifiStreaming |
from panda import Panda, PandaWifiStreaming |
||||||
from nose.tools import assert_less, assert_greater |
from nose.tools import assert_less, assert_greater |
||||||
|
|
||||||
|
# Reset the pandas before running tests |
||||||
|
def aaaa_reset_before_tests(): |
||||||
|
reset_pandas() |
||||||
|
|
||||||
@test_white |
@test_white |
||||||
@panda_type_to_serial |
@panda_type_to_serial |
||||||
def test_udp_doesnt_drop(serials=None): |
def test_udp_doesnt_drop(serials=None): |
||||||
connect_wifi(serials[0]) |
connect_wifi(serials[0]) |
||||||
|
|
||||||
p = Panda(serials[0]) |
p = Panda(serials[0]) |
||||||
|
|
||||||
|
# Start heartbeat |
||||||
|
start_heartbeat_thread(p) |
||||||
|
|
||||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||||
p.set_can_loopback(True) |
p.set_can_loopback(True) |
||||||
|
|
@ -0,0 +1,202 @@ |
|||||||
|
import os |
||||||
|
import time |
||||||
|
import random |
||||||
|
from panda import Panda |
||||||
|
from nose.tools import assert_equal, assert_less, assert_greater |
||||||
|
from .helpers import panda_jungle, start_heartbeat_thread, reset_pandas, time_many_sends, test_all_pandas, test_all_gen2_pandas, clear_can_buffers, panda_connect_and_init |
||||||
|
|
||||||
|
# Reset the pandas before running tests |
||||||
|
def aaaa_reset_before_tests(): |
||||||
|
reset_pandas() |
||||||
|
|
||||||
|
@test_all_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_send_recv(p): |
||||||
|
def test(p_send, p_recv): |
||||||
|
p_send.set_can_loopback(False) |
||||||
|
p_recv.set_can_loopback(False) |
||||||
|
|
||||||
|
p_send.can_send_many([(0x1ba, 0, b"message", 0)]*2) |
||||||
|
time.sleep(0.05) |
||||||
|
p_recv.can_recv() |
||||||
|
p_send.can_recv() |
||||||
|
|
||||||
|
busses = [0,1,2] |
||||||
|
|
||||||
|
for bus in busses: |
||||||
|
for speed in [100, 250, 500, 750, 1000]: |
||||||
|
p_send.set_can_speed_kbps(bus, speed) |
||||||
|
p_recv.set_can_speed_kbps(bus, speed) |
||||||
|
time.sleep(0.05) |
||||||
|
|
||||||
|
clear_can_buffers(p_send) |
||||||
|
clear_can_buffers(p_recv) |
||||||
|
|
||||||
|
comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) |
||||||
|
|
||||||
|
saturation_pct = (comp_kbps/speed) * 100.0 |
||||||
|
assert_greater(saturation_pct, 80) |
||||||
|
assert_less(saturation_pct, 100) |
||||||
|
|
||||||
|
print("two pandas bus {}, 100 messages at speed {:4d}, comp speed is {:7.2f}, percent {:6.2f}".format(bus, speed, comp_kbps, saturation_pct)) |
||||||
|
|
||||||
|
# Start heartbeat |
||||||
|
start_heartbeat_thread(p) |
||||||
|
|
||||||
|
# Set safety mode and power saving |
||||||
|
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||||
|
p.set_power_save(False) |
||||||
|
|
||||||
|
try: |
||||||
|
# Run tests in both directions |
||||||
|
test(p, panda_jungle) |
||||||
|
test(panda_jungle, p) |
||||||
|
except Exception as e: |
||||||
|
# Raise errors again, we don't want them to get lost |
||||||
|
raise e |
||||||
|
finally: |
||||||
|
# Set back to silent mode |
||||||
|
p.set_safety_mode(Panda.SAFETY_SILENT) |
||||||
|
|
||||||
|
@test_all_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_latency(p): |
||||||
|
def test(p_send, p_recv): |
||||||
|
p_send.set_can_loopback(False) |
||||||
|
p_recv.set_can_loopback(False) |
||||||
|
|
||||||
|
p_send.set_can_speed_kbps(0, 100) |
||||||
|
p_recv.set_can_speed_kbps(0, 100) |
||||||
|
time.sleep(0.05) |
||||||
|
|
||||||
|
p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)]*10) |
||||||
|
time.sleep(0.05) |
||||||
|
p_recv.can_recv() |
||||||
|
p_send.can_recv() |
||||||
|
|
||||||
|
busses = [0,1,2] |
||||||
|
|
||||||
|
for bus in busses: |
||||||
|
for speed in [100, 250, 500, 750, 1000]: |
||||||
|
p_send.set_can_speed_kbps(bus, speed) |
||||||
|
p_recv.set_can_speed_kbps(bus, speed) |
||||||
|
time.sleep(0.1) |
||||||
|
|
||||||
|
# clear can buffers |
||||||
|
clear_can_buffers(p_send) |
||||||
|
clear_can_buffers(p_recv) |
||||||
|
|
||||||
|
latencies = [] |
||||||
|
comp_kbps_list = [] |
||||||
|
saturation_pcts = [] |
||||||
|
|
||||||
|
num_messages = 100 |
||||||
|
|
||||||
|
for i in range(num_messages): |
||||||
|
st = time.time() |
||||||
|
p_send.can_send(0x1ab, b"message", bus) |
||||||
|
r = [] |
||||||
|
while len(r) < 1 and (time.time() - st) < 5: |
||||||
|
r = p_recv.can_recv() |
||||||
|
et = time.time() |
||||||
|
r_echo = [] |
||||||
|
while len(r_echo) < 1 and (time.time() - st) < 10: |
||||||
|
r_echo = p_send.can_recv() |
||||||
|
|
||||||
|
if len(r) == 0 or len(r_echo) == 0: |
||||||
|
print("r: {}, r_echo: {}".format(r, r_echo)) |
||||||
|
|
||||||
|
assert_equal(len(r),1) |
||||||
|
assert_equal(len(r_echo),1) |
||||||
|
|
||||||
|
et = (et - st)*1000.0 |
||||||
|
comp_kbps = (1+11+1+1+1+4+8*8+15+1+1+1+7) / et |
||||||
|
latency = et - ((1+11+1+1+1+4+8*8+15+1+1+1+7) / speed) |
||||||
|
|
||||||
|
assert_less(latency, 5.0) |
||||||
|
|
||||||
|
saturation_pct = (comp_kbps/speed) * 100.0 |
||||||
|
latencies.append(latency) |
||||||
|
comp_kbps_list.append(comp_kbps) |
||||||
|
saturation_pcts.append(saturation_pct) |
||||||
|
|
||||||
|
average_latency = sum(latencies)/num_messages |
||||||
|
assert_less(average_latency, 1.0) |
||||||
|
average_comp_kbps = sum(comp_kbps_list)/num_messages |
||||||
|
average_saturation_pct = sum(saturation_pcts)/num_messages |
||||||
|
|
||||||
|
print("two pandas bus {}, {} message average at speed {:4d}, latency is {:5.3f}ms, comp speed is {:7.2f}, percent {:6.2f}"\ |
||||||
|
.format(bus, num_messages, speed, average_latency, average_comp_kbps, average_saturation_pct)) |
||||||
|
|
||||||
|
# Start heartbeat |
||||||
|
start_heartbeat_thread(p) |
||||||
|
|
||||||
|
# Set safety mode and power saving |
||||||
|
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||||
|
p.set_power_save(False) |
||||||
|
|
||||||
|
try: |
||||||
|
# Run tests in both directions |
||||||
|
test(p, panda_jungle) |
||||||
|
test(panda_jungle, p) |
||||||
|
except Exception as e: |
||||||
|
# Raise errors again, we don't want them to get lost |
||||||
|
raise e |
||||||
|
finally: |
||||||
|
# Set back to silent mode |
||||||
|
p.set_safety_mode(Panda.SAFETY_SILENT) |
||||||
|
|
||||||
|
|
||||||
|
@test_all_gen2_pandas |
||||||
|
@panda_connect_and_init |
||||||
|
def test_gen2_loopback(p): |
||||||
|
def test(p_send, p_recv): |
||||||
|
for bus in range(4): |
||||||
|
obd = False |
||||||
|
if bus == 3: |
||||||
|
obd = True |
||||||
|
bus = 1 |
||||||
|
|
||||||
|
# Clear buses |
||||||
|
clear_can_buffers(p_send) |
||||||
|
clear_can_buffers(p_recv) |
||||||
|
|
||||||
|
# Send a random string |
||||||
|
addr = random.randint(1, 2000) |
||||||
|
string = b"test"+os.urandom(4) |
||||||
|
p_send.set_obd(obd) |
||||||
|
p_recv.set_obd(obd) |
||||||
|
time.sleep(0.2) |
||||||
|
p_send.can_send(addr, string, bus) |
||||||
|
time.sleep(0.2) |
||||||
|
|
||||||
|
content = p_recv.can_recv() |
||||||
|
|
||||||
|
# Check amount of messages |
||||||
|
assert len(content) == 1 |
||||||
|
|
||||||
|
# Check content |
||||||
|
assert content[0][0] == addr and content[0][2] == string |
||||||
|
|
||||||
|
# Check bus |
||||||
|
assert content[0][3] == bus |
||||||
|
|
||||||
|
print("Bus:", bus, "OBD:", obd, "OK") |
||||||
|
|
||||||
|
# Start heartbeat |
||||||
|
start_heartbeat_thread(p) |
||||||
|
|
||||||
|
# Set safety mode and power saving |
||||||
|
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||||
|
p.set_power_save(False) |
||||||
|
|
||||||
|
try: |
||||||
|
# Run tests in both directions |
||||||
|
test(p, panda_jungle) |
||||||
|
test(panda_jungle, p) |
||||||
|
except Exception as e: |
||||||
|
# Raise errors again, we don't want them to get lost |
||||||
|
raise e |
||||||
|
finally: |
||||||
|
# Set back to silent mode |
||||||
|
p.set_safety_mode(Panda.SAFETY_SILENT) |
@ -0,0 +1,25 @@ |
|||||||
|
import time |
||||||
|
from multiprocessing import Process |
||||||
|
|
||||||
|
# Note: this does not return any return values of the function, just the exit status |
||||||
|
INTERVAL = 0.1 |
||||||
|
def run_with_timeout(timeout, fn, *kwargs): |
||||||
|
def runner(fn, kwargs): |
||||||
|
try: |
||||||
|
fn(*kwargs) |
||||||
|
except Exception as e: |
||||||
|
print(e) |
||||||
|
raise e |
||||||
|
|
||||||
|
process = Process(target=runner, args=(fn, kwargs)) |
||||||
|
process.start() |
||||||
|
|
||||||
|
counter = 0 |
||||||
|
while process.is_alive(): |
||||||
|
time.sleep(INTERVAL) |
||||||
|
counter+=1 |
||||||
|
if (counter * INTERVAL) > timeout: |
||||||
|
process.terminate() |
||||||
|
raise TimeoutError("Function timed out!") |
||||||
|
if process.exitcode != 0: |
||||||
|
raise RuntimeError("Test failed with exit code: ", str(process.exitcode)) |
@ -0,0 +1,85 @@ |
|||||||
|
import os |
||||||
|
FNULL = open(os.devnull, 'w') |
||||||
|
def _connect_wifi(dongle_id, pw, insecure_okay=False): |
||||||
|
ssid = "panda-" + dongle_id |
||||||
|
|
||||||
|
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||||
|
if not r: |
||||||
|
# Can already ping, try connecting on wifi |
||||||
|
try: |
||||||
|
p = Panda("WIFI") |
||||||
|
p.get_serial() |
||||||
|
print("Already connected") |
||||||
|
return |
||||||
|
except: |
||||||
|
pass |
||||||
|
|
||||||
|
print("WIFI: connecting to %s" % ssid) |
||||||
|
|
||||||
|
while 1: |
||||||
|
if sys.platform == "darwin": |
||||||
|
os.system("networksetup -setairportnetwork en0 %s %s" % (ssid, pw)) |
||||||
|
else: |
||||||
|
wlan_interface = subprocess.check_output(["sh", "-c", "iw dev | awk '/Interface/ {print $2}'"]).strip().decode('utf8') |
||||||
|
cnt = 0 |
||||||
|
MAX_TRIES = 10 |
||||||
|
while cnt < MAX_TRIES: |
||||||
|
print("WIFI: scanning %d" % cnt) |
||||||
|
os.system("iwlist %s scanning > /dev/null" % wlan_interface) |
||||||
|
os.system("nmcli device wifi rescan") |
||||||
|
wifi_networks = [x.decode("utf8") for x in subprocess.check_output(["nmcli","dev", "wifi", "list"]).split(b"\n")] |
||||||
|
wifi_scan = [x for x in wifi_networks if ssid in x] |
||||||
|
if len(wifi_scan) != 0: |
||||||
|
break |
||||||
|
time.sleep(0.1) |
||||||
|
# MAX_TRIES tries, ~10 seconds max |
||||||
|
cnt += 1 |
||||||
|
assert cnt < MAX_TRIES |
||||||
|
if "-pair" in wifi_scan[0]: |
||||||
|
os.system("nmcli d wifi connect %s-pair" % (ssid)) |
||||||
|
connect_cnt = 0 |
||||||
|
MAX_TRIES = 100 |
||||||
|
while connect_cnt < MAX_TRIES: |
||||||
|
connect_cnt += 1 |
||||||
|
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||||
|
if r: |
||||||
|
print("Waiting for panda to ping...") |
||||||
|
time.sleep(0.5) |
||||||
|
else: |
||||||
|
break |
||||||
|
if insecure_okay: |
||||||
|
break |
||||||
|
# fetch webpage |
||||||
|
print("connecting to insecure network to secure") |
||||||
|
try: |
||||||
|
r = requests.get("http://192.168.0.10/") |
||||||
|
except requests.ConnectionError: |
||||||
|
r = requests.get("http://192.168.0.10/") |
||||||
|
assert r.status_code==200 |
||||||
|
|
||||||
|
print("securing") |
||||||
|
try: |
||||||
|
r = requests.get("http://192.168.0.10/secure", timeout=0.01) |
||||||
|
except requests.exceptions.Timeout: |
||||||
|
print("timeout http request to secure") |
||||||
|
pass |
||||||
|
else: |
||||||
|
ret = os.system("nmcli d wifi connect %s password %s" % (ssid, pw)) |
||||||
|
if os.WEXITSTATUS(ret) == 0: |
||||||
|
#check ping too |
||||||
|
ping_ok = False |
||||||
|
connect_cnt = 0 |
||||||
|
MAX_TRIES = 10 |
||||||
|
while connect_cnt < MAX_TRIES: |
||||||
|
connect_cnt += 1 |
||||||
|
r = subprocess.call(["ping", "-W", "4", "-c", "1", "192.168.0.10"], stdout=FNULL, stderr=subprocess.STDOUT) |
||||||
|
if r: |
||||||
|
print("Waiting for panda to ping...") |
||||||
|
time.sleep(0.1) |
||||||
|
else: |
||||||
|
ping_ok = True |
||||||
|
break |
||||||
|
if ping_ok: |
||||||
|
break |
||||||
|
|
||||||
|
# TODO: confirm that it's connected to the right panda |
@ -0,0 +1,50 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import matplotlib.pyplot as plt # pylint: disable=import-error |
||||||
|
|
||||||
|
HASHING_PRIME = 23 |
||||||
|
REGISTER_MAP_SIZE = 0x3FF |
||||||
|
BYTES_PER_REG = 4 |
||||||
|
|
||||||
|
# From ST32F413 datasheet |
||||||
|
REGISTER_ADDRESS_REGIONS = [ |
||||||
|
(0x40000000, 0x40007FFF), |
||||||
|
(0x40010000, 0x400107FF), |
||||||
|
(0x40011000, 0x400123FF), |
||||||
|
(0x40012C00, 0x40014BFF), |
||||||
|
(0x40015000, 0x400153FF), |
||||||
|
(0x40015800, 0x40015BFF), |
||||||
|
(0x40016000, 0x400167FF), |
||||||
|
(0x40020000, 0x40021FFF), |
||||||
|
(0x40023000, 0x400233FF), |
||||||
|
(0x40023800, 0x40023FFF), |
||||||
|
(0x40026000, 0x400267FF), |
||||||
|
(0x50000000, 0x5003FFFF), |
||||||
|
(0x50060000, 0x500603FF), |
||||||
|
(0x50060800, 0x50060BFF), |
||||||
|
(0x50060800, 0x50060BFF), |
||||||
|
(0xE0000000, 0xE00FFFFF) |
||||||
|
] |
||||||
|
|
||||||
|
def hash(reg_addr): |
||||||
|
return (((reg_addr >> 16) ^ ((((reg_addr + 1) & 0xFFFF) * HASHING_PRIME) & 0xFFFF)) & REGISTER_MAP_SIZE) |
||||||
|
|
||||||
|
# Calculate hash for each address |
||||||
|
hashes = [] |
||||||
|
double_hashes = [] |
||||||
|
for (start_addr, stop_addr) in REGISTER_ADDRESS_REGIONS: |
||||||
|
for addr in range(start_addr, stop_addr+1, BYTES_PER_REG): |
||||||
|
h = hash(addr) |
||||||
|
hashes.append(h) |
||||||
|
double_hashes.append(hash(h)) |
||||||
|
|
||||||
|
# Make histograms |
||||||
|
plt.subplot(2, 1, 1) |
||||||
|
plt.hist(hashes, bins=REGISTER_MAP_SIZE) |
||||||
|
plt.title("Number of collisions per hash") |
||||||
|
plt.xlabel("Address") |
||||||
|
|
||||||
|
plt.subplot(2, 1, 2) |
||||||
|
plt.hist(double_hashes, bins=REGISTER_MAP_SIZE) |
||||||
|
plt.title("Number of collisions per double hash") |
||||||
|
plt.xlabel("Address") |
||||||
|
plt.show() |
@ -0,0 +1,35 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import time |
||||||
|
import _thread |
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) |
||||||
|
from panda import Panda |
||||||
|
|
||||||
|
# This script is intended to be used in conjunction with the echo_loopback_test.py test script from panda jungle. |
||||||
|
# It sends a reversed response back for every message received containing b"test". |
||||||
|
|
||||||
|
def heartbeat_thread(p): |
||||||
|
while True: |
||||||
|
try: |
||||||
|
p.send_heartbeat() |
||||||
|
time.sleep(1) |
||||||
|
except: |
||||||
|
break |
||||||
|
|
||||||
|
# Resend every CAN message that has been received on the same bus, but with the data reversed |
||||||
|
if __name__ == "__main__": |
||||||
|
p = Panda() |
||||||
|
_thread.start_new_thread(heartbeat_thread, (p,)) |
||||||
|
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) |
||||||
|
p.set_power_save(False) |
||||||
|
|
||||||
|
while True: |
||||||
|
incoming = p.can_recv() |
||||||
|
for message in incoming: |
||||||
|
address, notused, data, bus = message |
||||||
|
if b'test' in data: |
||||||
|
p.can_send(address, data[::-1], bus) |
||||||
|
|
||||||
|
|
@ -0,0 +1 @@ |
|||||||
|
cppcheck/ |
@ -0,0 +1,36 @@ |
|||||||
|
from panda.tests.safety import libpandasafety_py |
||||||
|
|
||||||
|
def make_msg(bus, addr, length=8): |
||||||
|
to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') |
||||||
|
if addr >= 0x800: |
||||||
|
to_send[0].RIR = (addr << 3) | 5 |
||||||
|
else: |
||||||
|
to_send[0].RIR = (addr << 21) | 1 |
||||||
|
to_send[0].RDTR = length |
||||||
|
to_send[0].RDTR |= bus << 4 |
||||||
|
|
||||||
|
return to_send |
||||||
|
|
||||||
|
def test_relay_malfunction(test, addr): |
||||||
|
# input is a test class and the address that, if seen on bus 0, triggers |
||||||
|
# the relay_malfunction protection logic: both tx_hook and fwd_hook are |
||||||
|
# expected to return failure |
||||||
|
test.assertFalse(test.safety.get_relay_malfunction()) |
||||||
|
test.safety.safety_rx_hook(make_msg(0, addr, 8)) |
||||||
|
test.assertTrue(test.safety.get_relay_malfunction()) |
||||||
|
for a in range(1, 0x800): |
||||||
|
for b in range(0, 3): |
||||||
|
test.assertFalse(test.safety.safety_tx_hook(make_msg(b, a, 8))) |
||||||
|
test.assertEqual(-1, test.safety.safety_fwd_hook(b, make_msg(b, a, 8))) |
||||||
|
|
||||||
|
def test_manually_enable_controls_allowed(test): |
||||||
|
test.safety.set_controls_allowed(1) |
||||||
|
test.assertTrue(test.safety.get_controls_allowed()) |
||||||
|
test.safety.set_controls_allowed(0) |
||||||
|
test.assertFalse(test.safety.get_controls_allowed()) |
||||||
|
|
||||||
|
def test_spam_can_buses(test, TX_MSGS): |
||||||
|
for addr in range(1, 0x800): |
||||||
|
for bus in range(0, 4): |
||||||
|
if all(addr != m[0] or bus != m[1] for m in TX_MSGS): |
||||||
|
test.assertFalse(test.safety.safety_tx_hook(make_msg(bus, addr, 8))) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue