diff --git a/SConscript b/SConscript index cd974b8104b..14447364ca0 100644 --- a/SConscript +++ b/SConscript @@ -64,10 +64,10 @@ def build_project(project_name, project, main, extra_flags): project_dir = Dir(f'./board/obj/{project_name}/') flags = project["FLAGS"] + extra_flags + common_flags + [ - "-Wall", - "-Wextra", + #"-Wall", + #"-Wextra", "-Wstrict-prototypes", - "-Werror", + #"-Werror", "-mlittle-endian", "-mthumb", "-nostdlib", diff --git a/board/config.h b/board/config.h index 1b7d938cc81..d488a31f738 100644 --- a/board/config.h +++ b/board/config.h @@ -5,7 +5,7 @@ //#define DEBUG //#define DEBUG_UART //#define DEBUG_USB -//#define DEBUG_SPI +#define DEBUG_SPI //#define DEBUG_FAULTS //#define DEBUG_COMMS //#define DEBUG_FAN diff --git a/board/drivers/interrupts.h b/board/drivers/interrupts.h index 1188a8ec07b..912bdc0e8b5 100644 --- a/board/drivers/interrupts.h +++ b/board/drivers/interrupts.h @@ -15,6 +15,13 @@ static uint32_t busy_time = 0U; float interrupt_load = 0.0f; void handle_interrupt(IRQn_Type irq_type){ + if (irq_type != 0x3a && irq_type != 0x3b && irq_type != 0x54) { + print("irq: "); puth(irq_type); print("\n"); + } + + interrupts[irq_type].handler(); + return; + static uint8_t interrupt_depth = 0U; static uint32_t last_time = 0U; ENTER_CRITICAL(); @@ -46,6 +53,8 @@ void handle_interrupt(IRQn_Type irq_type){ // Every second void interrupt_timer_handler(void) { + INTERRUPT_TIMER->SR = 0; + return; if (INTERRUPT_TIMER->SR != 0U) { for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { // Log IRQ call rate faults diff --git a/board/drivers/spi.h b/board/drivers/spi.h index d291fe06a8d..4b3b791b6c7 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -1,5 +1,6 @@ #pragma once + #include "board/drivers/spi_declarations.h" #include "board/crc.h" @@ -8,7 +9,6 @@ uint8_t spi_buf_tx[SPI_BUF_SIZE]; uint16_t spi_error_count = 0; -static uint8_t spi_state = SPI_STATE_HEADER; static uint16_t spi_data_len_mosi; static bool spi_can_tx_ready = false; static const unsigned char version_text[] = "VERSION"; @@ -62,8 +62,7 @@ void spi_init(void) { llspi_init(); // Start the first packet! - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + llspi_mosi_dma(spi_buf_rx); } static bool validate_checksum(const uint8_t *data, uint16_t len) { @@ -77,152 +76,108 @@ static bool validate_checksum(const uint8_t *data, uint16_t len) { void spi_rx_done(void) { uint16_t response_len = 0U; - uint8_t next_rx_state = SPI_STATE_HEADER_NACK; bool checksum_valid = false; static uint8_t spi_endpoint; static uint16_t spi_data_len_miso; - // parse header - spi_endpoint = spi_buf_rx[1]; - spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; - spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; - if (memcmp(spi_buf_rx, version_text, 7) == 0) { response_len = spi_version_packet(spi_buf_tx); - next_rx_state = SPI_STATE_HEADER_NACK;; - } else if (spi_state == SPI_STATE_HEADER) { - checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); - if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { - // response: ACK and start receiving data portion - spi_buf_tx[0] = SPI_HACK; - next_rx_state = SPI_STATE_HEADER_ACK; - response_len = 1U; - } else { - // response: NACK and reset state machine - #ifdef DEBUG_SPI - print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); - #endif - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; - response_len = 1U; + } else { + // parse header + spi_endpoint = spi_buf_rx[1]; + spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2]; + spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4]; + + // Validate header + bool header_ok = false; + if (spi_buf_rx[0] == SPI_SYNC_BYTE) { + header_ok = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE); } - } else if (spi_state == SPI_STATE_DATA_RX) { - // We got everything! Based on the endpoint specified, call the appropriate handler + bool response_ack = false; - checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); - if (checksum_valid) { - if (spi_endpoint == 0U) { - if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { - ControlPacket_t ctrl = {0}; - (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); - response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); - response_ack = true; - } else { - print("SPI: insufficient data for control handler\n"); - } - } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { - if (spi_data_len_mosi == 0U) { - response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); - response_ack = true; - } else { - print("SPI: did not expect data for can_read\n"); - } - } else if (spi_endpoint == 2U) { - comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); - response_ack = true; - } else if (spi_endpoint == 3U) { - if (spi_data_len_mosi > 0U) { - if (spi_can_tx_ready) { - spi_can_tx_ready = false; - comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + if (header_ok) { + // Validate data+checksum. Data checksum byte sits immediately after data + checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U); + + if (checksum_valid) { + // Dispatch + if (spi_endpoint == 0U) { + if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { + ControlPacket_t ctrl = {0}; + (void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); + response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); response_ack = true; } else { - response_ack = false; - print("SPI: CAN NACK\n"); + print("SPI: insufficient data for control handler\n"); } + } else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) { + if (spi_data_len_mosi == 0U) { + response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso); + response_ack = true; + } else { + print("SPI: did not expect data for can_read\n"); + } + } else if (spi_endpoint == 2U) { + comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else if (spi_endpoint == 3U) { + if (spi_data_len_mosi > 0U) { + if (spi_can_tx_ready) { + spi_can_tx_ready = false; + comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + response_ack = true; + } else { + response_ack = false; + print("SPI: CAN NACK\n"); + } + } else { + print("SPI: expected data for can_write\n"); + } + } else if (spi_endpoint == 0xABU) { + // test endpoint: mimics panda -> device transfer + response_len = spi_data_len_miso; + response_ack = true; + } else if (spi_endpoint == 0xACU) { + // test endpoint: mimics device -> panda transfer (with NACK) + response_ack = false; } else { - print("SPI: did expect data for can_write\n"); + print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); } - } else if (spi_endpoint == 0xABU) { - // test endpoint: mimics panda -> device transfer - response_len = spi_data_len_miso; - response_ack = true; - } else if (spi_endpoint == 0xACU) { - // test endpoint: mimics device -> panda transfer (with NACK) - response_ack = false; - } else { - print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); } - } else { - // Checksum was incorrect - response_ack = false; - #ifdef DEBUG_SPI - print("- incorrect data checksum "); - puth4(spi_data_len_mosi); - print("\n"); - hexdump(spi_buf_rx, SPI_HEADER_SIZE); - hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64)); - print("\n"); - #endif + } + + if (!header_ok || !checksum_valid) { + print("error: "); hexdump(spi_buf_rx, 24); print("\n"); + if (false) puth4(4); + spi_error_count += 1U; } if (!response_ack) { spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_STATE_HEADER_NACK; response_len = 1U; } else { // Setup response header - spi_buf_tx[0] = SPI_DACK; + spi_buf_tx[0] = SPI_ACK; spi_buf_tx[1] = response_len & 0xFFU; spi_buf_tx[2] = (response_len >> 8) & 0xFFU; - // Add checksum + // Add checksum over header + payload uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < (response_len + 3U); i++) { + for (uint16_t i = 0U; i < (response_len + 3U); i++) { checksum ^= spi_buf_tx[i]; } spi_buf_tx[response_len + 3U] = checksum; + // response_len now refers to header + payload + checksum response_len += 4U; - - next_rx_state = SPI_STATE_DATA_TX; } - } else { - print("SPI: RX unexpected state: "); puth(spi_state); print("\n"); } - // send out response - if (response_len == 0U) { - print("SPI: no response\n"); - spi_buf_tx[0] = SPI_NACK; - spi_state = SPI_STATE_HEADER_NACK; - response_len = 1U; - } + // send out the response llspi_miso_dma(spi_buf_tx, response_len); - - spi_state = next_rx_state; - if (!checksum_valid) { - spi_error_count += 1U; - } } -void spi_tx_done(bool reset) { - if ((spi_state == SPI_STATE_HEADER_NACK) || reset) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else if (spi_state == SPI_STATE_HEADER_ACK) { - // ACK was sent, queue up the RX buf for the data + checksum - spi_state = SPI_STATE_DATA_RX; - llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U); - } else if (spi_state == SPI_STATE_DATA_TX) { - // Reset state - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else { - spi_state = SPI_STATE_HEADER; - llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - print("SPI: TX unexpected state: "); puth(spi_state); print("\n"); - } +void spi_tx_done() { + llspi_mosi_dma(spi_buf_rx); } void can_tx_comms_resume_spi(void) { diff --git a/board/drivers/spi_declarations.h b/board/drivers/spi_declarations.h index 23254f0e87a..f3397a87579 100644 --- a/board/drivers/spi_declarations.h +++ b/board/drivers/spi_declarations.h @@ -2,43 +2,39 @@ #include "board/crc.h" -#define SPI_TIMEOUT_US 10000U +#define SPI_TIMEOUT_US 1000U // got max rate from hitting a non-existent endpoint // in a tight loop, plus some buffer #define SPI_IRQ_RATE 16000U -#define SPI_BUF_SIZE 4096U +#define SPI_BUF_SIZE 128U // H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 __attribute__((section(".sram12"))) extern uint8_t spi_buf_rx[SPI_BUF_SIZE]; __attribute__((section(".sram12"))) extern uint8_t spi_buf_tx[SPI_BUF_SIZE]; +// Protocol constants #define SPI_CHECKSUM_START 0xABU #define SPI_SYNC_BYTE 0x5AU -#define SPI_HACK 0x79U -#define SPI_DACK 0x85U +#define SPI_ACK 0x85U #define SPI_NACK 0x1FU -// SPI states -enum { - SPI_STATE_HEADER, - SPI_STATE_HEADER_ACK, - SPI_STATE_HEADER_NACK, - SPI_STATE_DATA_RX, - SPI_STATE_DATA_RX_ACK, - SPI_STATE_DATA_TX -}; - extern uint16_t spi_error_count; +// Fixed frame contains a 7-byte header followed by data and a 1-byte data checksum. +// header: [SYNC(1) | EP(1) | MOSI_LEN(2 LE) | MISO_MAX(2 LE) | HDR_CKSUM(1)] +// data: MOSI_LEN bytes starting at offset SPI_HEADER_SIZE +// data checksum byte immediately following data. +// The device responds in the next frame with: +// [DACK(1) | RESP_LEN(2 LE) | RESP_DATA | RESP_CKSUM(1)] #define SPI_HEADER_SIZE 7U // low level SPI prototypes void llspi_init(void); -void llspi_mosi_dma(uint8_t *addr, int len); +void llspi_mosi_dma(uint8_t *addr); void llspi_miso_dma(uint8_t *addr, int len); void can_tx_comms_resume_spi(void); void spi_init(void); void spi_rx_done(void); -void spi_tx_done(bool reset); +void spi_tx_done(void); diff --git a/board/drivers/timers.h b/board/drivers/timers.h index 4c046a2b49b..14aea7ff0bc 100644 --- a/board/drivers/timers.h +++ b/board/drivers/timers.h @@ -22,10 +22,10 @@ void interrupt_timer_init(void) { register_set(&(INTERRUPT_TIMER->DIER), TIM_DIER_UIE, 0x5F5FU); register_set(&(INTERRUPT_TIMER->CR1), TIM_CR1_CEN, 0x3FU); INTERRUPT_TIMER->SR = 0; - NVIC_EnableIRQ(INTERRUPT_TIMER_IRQ); + NVIC_DisableIRQ(INTERRUPT_TIMER_IRQ); } void tick_timer_init(void) { - timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); - NVIC_EnableIRQ(TICK_TIMER_IRQ); + //timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); + NVIC_DisableIRQ(TICK_TIMER_IRQ); } diff --git a/board/drivers/uart.h b/board/drivers/uart.h index aeacf84b851..760428e4a56 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -136,7 +136,7 @@ static void puth4(unsigned int i) { #endif #if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG_USB) || defined(DEBUG_COMMS) -static void hexdump(const void *a, int l) { +void hexdump(const void *a, int l) { if (a != NULL) { for (int i=0; i < l; i++) { if ((i != 0) && ((i & 0xf) == 0)) print("\n"); diff --git a/board/drivers/uart_declarations.h b/board/drivers/uart_declarations.h index 14ec26110f9..ad82c39fc85 100644 --- a/board/drivers/uart_declarations.h +++ b/board/drivers/uart_declarations.h @@ -35,5 +35,5 @@ void puth(unsigned int i); static void puth4(unsigned int i); #endif #if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS) -static void hexdump(const void *a, int l); +void hexdump(const void *a, int l); #endif diff --git a/board/main.c b/board/main.c index ac0fda9ba43..a04ace977c5 100644 --- a/board/main.c +++ b/board/main.c @@ -121,6 +121,11 @@ static void tick_handler(void) { static bool relay_malfunction_prev = false; if (TICK_TIMER->SR != 0U) { + /* + uint32_t sr = FLASH->SR1; + uint32_t cr = FLASH->CR1; + print("sr "); puth4(sr); print(" cr "); puth4(cr); print(" lock "); puth(flash_is_locked()); print("\n"); + */ // siren current_board->set_siren((loop_counter & 1U) && (siren_enabled || (siren_countdown > 0U))); diff --git a/board/stm32h7/llspi.h b/board/stm32h7/llspi.h index 05f8e22f9a8..151538a6a2d 100644 --- a/board/stm32h7/llspi.h +++ b/board/stm32h7/llspi.h @@ -1,9 +1,12 @@ +#include "board/drivers/spi_declarations.h" + // master -> panda DMA start -void llspi_mosi_dma(uint8_t *addr, int len) { +void llspi_mosi_dma(uint8_t *addr) { // disable DMA + SPI register_clear_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); DMA2_Stream2->CR &= ~DMA_SxCR_EN; register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); + register_set(&(SPI4->UDRDR), 0xdd, 0xFFFFU); // set under-run value for debugging // drain the bus while ((SPI4->SR & SPI_SR_RXP) != 0U) { @@ -17,24 +20,31 @@ void llspi_mosi_dma(uint8_t *addr, int len) { // setup destination and length register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU); - DMA2_Stream2->NDTR = len; + DMA2_Stream2->NDTR = SPI_BUF_SIZE; // enable DMA + SPI DMA2_Stream2->CR |= DMA_SxCR_EN; register_set_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN); register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); + SPI4->TXDR = 0xdddddddd; // match the UDR bytes } +static uint32_t dma_len = 0; +static uint32_t dma_start_ts = 0; + // panda -> master DMA start void llspi_miso_dma(uint8_t *addr, int len) { // disable DMA + SPI DMA2_Stream3->CR &= ~DMA_SxCR_EN; register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); + register_set(&(SPI4->UDRDR), 0xcc, 0xFFFFU); // set under-run value for debugging + if (len < 0x44) len = 0x44; // setup source and length register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU); DMA2_Stream3->NDTR = len; + dma_len = (uint32_t)len; // clear under-run while we were reading SPI4->IFCR |= (0x1FFU << 3U); @@ -46,8 +56,10 @@ void llspi_miso_dma(uint8_t *addr, int len) { register_set_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); DMA2_Stream3->CR |= DMA_SxCR_EN; register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); + dma_start_ts = microsecond_timer_get(); } +static uint32_t dma_done_ts = 0; static bool spi_tx_dma_done = false; // master -> panda DMA finished static void DMA2_Stream2_IRQ_Handler(void) { @@ -59,10 +71,30 @@ static void DMA2_Stream2_IRQ_Handler(void) { // panda -> master DMA finished static void DMA2_Stream3_IRQ_Handler(void) { + dma_done_ts = microsecond_timer_get(); ENTER_CRITICAL(); DMA2->LIFCR = DMA_LIFCR_CTCIF3; - spi_tx_dma_done = true; + //spi_tx_dma_done = true; + + bool timed_out = false; + while (!(SPI4->SR & SPI_SR_TXC)) { + if (get_ts_elapsed(microsecond_timer_get(), dma_done_ts) > SPI_TIMEOUT_US) { + timed_out = true; + break; + } + } + uint32_t txc = get_ts_elapsed(microsecond_timer_get(), dma_done_ts); + uint32_t es = get_ts_elapsed(dma_done_ts, dma_start_ts); + print("DMA end-start: 0x"); puth4(es); print(", TXC 0x"); puth4(txc); print(", tot 0x"); puth4(txc+es); print(", len 0x"); puth4(dma_len); print("\n"); + if (timed_out) print("timed out!!\n"); + + // clear out FIFO + volatile uint8_t dat = SPI4->TXDR; + (void)dat; + (void)dma_start_ts; + (void)dat; + spi_tx_done(); EXIT_CRITICAL(); } @@ -73,8 +105,11 @@ static void SPI4_IRQ_Handler(void) { SPI4->IFCR |= (0x1FFU << 3U); if (spi_tx_dma_done && ((SPI4->SR & SPI_SR_TXC) != 0U)) { + uint32_t now = microsecond_timer_get(); + uint32_t diff = get_ts_elapsed(now, dma_done_ts); + print("diff: 0x"); puth4(diff); print("\n"); spi_tx_dma_done = false; - spi_tx_done(false); + spi_tx_done(); } } @@ -96,8 +131,7 @@ void llspi_init(void) { // Enable SPI register_set(&(SPI4->IER), 0, 0x3FFU); - register_set(&(SPI4->CFG1), (7U << SPI_CFG1_DSIZE_Pos), SPI_CFG1_DSIZE_Msk); - register_set(&(SPI4->UDRDR), 0xcd, 0xFFFFU); // set under-run value for debugging + register_set(&(SPI4->CFG1), (7U << SPI_CFG1_DSIZE_Pos) | (0b01 << SPI_CFG1_UDRDET_Pos), SPI_CFG1_DSIZE_Msk | SPI_CFG1_UDRDET_Msk); register_set(&(SPI4->CR1), SPI_CR1_SPE, 0xFFFFU); register_set(&(SPI4->CR2), 0, 0xFFFFU); diff --git a/board/stm32h7/lluart.h b/board/stm32h7/lluart.h index e18f1e9f6f3..8a79182c3b0 100644 --- a/board/stm32h7/lluart.h +++ b/board/stm32h7/lluart.h @@ -97,6 +97,6 @@ void uart_init(uart_ring *q, unsigned int baud) { q->uart->CR1 |= USART_CR1_RXNEIE; // Enable UART interrupts - NVIC_EnableIRQ(UART7_IRQn); + //NVIC_EnableIRQ(UART7_IRQn); } } diff --git a/board/stm32h7/llusb.h b/board/stm32h7/llusb.h index f1bcf16ad5e..4e0e15d3257 100644 --- a/board/stm32h7/llusb.h +++ b/board/stm32h7/llusb.h @@ -5,7 +5,7 @@ USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; static void OTG_HS_IRQ_Handler(void) { NVIC_DisableIRQ(OTG_HS_IRQn); usb_irqhandler(); - NVIC_EnableIRQ(OTG_HS_IRQn); + NVIC_DisableIRQ(OTG_HS_IRQn); } void usb_init(void) { @@ -61,7 +61,7 @@ void usb_init(void) { USBx->GINTMSK = 0U; // Clear any pending interrupts USBx->GINTSTS = 0xBFFFFFFFU; - // Enable interrupts matching to the Device mode ONLY + // Disable interrupts matching to the Device mode ONLY USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT | USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM | USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | @@ -69,11 +69,11 @@ void usb_init(void) { // Set USB Turnaround time USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); - // Enables the controller's Global Int in the AHB Config reg + // Disables the controller's Global Int in the AHB Config reg USBx->GAHBCFG |= USB_OTG_GAHBCFG_GINT; // Soft disconnect disable: USBx_DEVICE->DCTL &= ~(USB_OTG_DCTL_SDIS); // enable the IRQ - NVIC_EnableIRQ(OTG_HS_IRQn); + NVIC_DisableIRQ(OTG_HS_IRQn); } diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index 8eb384307f3..f1b79687e46 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -92,7 +92,7 @@ void peripherals_init(void) { RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN; // Supplemental - RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DAC DMA + //RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DAC DMA RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // SPI DMA RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; RCC->AHB4ENR |= RCC_AHB4ENR_BDMAEN; // Audio DMA diff --git a/python/__init__.py b/python/__init__.py index aa5f49dd6fb..cf936bdacc1 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -147,9 +147,10 @@ def __init__(self, serial: str | None = None, claim: bool = True, disable_checks self._can_speed_kbps = can_speed_kbps if cli and serial is None: - self._connect_serial = self._cli_select_panda() + #self._connect_serial = self._cli_select_panda() + self._connect_serial = None else: - self._connect_serial = serial + self._connect_serial = serial # connect and set mcu type self.connect(claim) @@ -745,7 +746,7 @@ def serial_read(self, port_number): ret = [] while 1: lret = bytes(self._handle.controlRead(Panda.REQUEST_IN, 0xe0, port_number, 0, 0x40)) - if len(lret) == 0: + if len(lret) == 0 or len(ret) > 1024: break ret.append(lret) return b''.join(ret) diff --git a/python/spi.py b/python/spi.py index d79440718e2..de1711f37b2 100644 --- a/python/spi.py +++ b/python/spi.py @@ -20,14 +20,14 @@ # Constants SYNC = 0x5A HACK = 0x79 -DACK = 0x85 +ACK = 0x85 NACK = 0x1F CHECKSUM_START = 0xAB MIN_ACK_TIMEOUT_MS = 100 MAX_XFER_RETRY_COUNT = 5 -SPI_BUF_SIZE = 4096 # from panda/board/drivers/spi.h +SPI_BUF_SIZE = 128 # from panda/board/drivers/spi.h XFER_SIZE = SPI_BUF_SIZE - 0x40 # give some room for SPI protocol overhead DEV_PATH = "/dev/spidev0.0" @@ -117,65 +117,57 @@ class PandaSpiHandle(BaseHandle): def __init__(self) -> None: self.dev = SpiDevice() + self.no_retry = "NO_RETRY" in os.environ - # helpers def _calc_checksum(self, data: bytes) -> int: cksum = CHECKSUM_START for b in data: cksum ^= b return cksum - def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes: - timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3 - - start = time.monotonic() - while (timeout == 0) or ((time.monotonic() - start) < timeout_s): - dat = spi.xfer2([tx, ] * length) - if dat[0] == ack_val: - return bytes(dat) - elif dat[0] == NACK: - raise PandaSpiNackResponse - - raise PandaSpiMissingAck - def _transfer_spidev(self, spi, endpoint: int, data, timeout: int, max_rx_len: int = 1000, expect_disconnect: bool = False) -> bytes: max_rx_len = max(USBPACKET_MAX_SIZE, max_rx_len) - logger.debug("- send header") - packet = self.HEADER.pack(SYNC, endpoint, len(data), max_rx_len) - packet += bytes([self._calc_checksum(packet), ]) - spi.xfer2(packet) - - logger.debug("- waiting for header ACK") - self._wait_for_ack(spi, HACK, MIN_ACK_TIMEOUT_MS, 0x11) - - logger.debug("- sending data") - packet = bytes([*data, self._calc_checksum(data)]) - spi.xfer2(packet) - + # *** TX to panda *** + frame = bytearray(b"\xea" * SPI_BUF_SIZE) + frame[0:self.HEADER.size] = self.HEADER.pack(SYNC, endpoint, len(data), max_rx_len) + frame[self.HEADER.size] = self._calc_checksum(frame[:self.HEADER.size]) + data_offset = self.HEADER.size + 1 + if len(data) > 0: + frame[data_offset:data_offset+len(data)] = data + frame[data_offset + len(data)] = self._calc_checksum(data) + spi.xfer2(frame) + #spi.xfer2(frame, 50_000_000, 1000, 8) + + # *** RX from panda *** if expect_disconnect: logger.debug("- expecting disconnect, returning") return b"" - else: - logger.debug("- waiting for data ACK") - preread_len = USBPACKET_MAX_SIZE + 1 # read enough for a controlRead - dat = self._wait_for_ack(spi, DACK, timeout, 0x13, length=3 + preread_len) - - # get response length, then response - response_len = struct.unpack(" max_rx_len: - raise PandaSpiException(f"response length greater than max ({max_rx_len} {response_len})") - - # read rest - remaining = (response_len + 1) - preread_len - if remaining > 0: - dat += bytes(spi.readbytes(remaining)) - - dat = dat[:3 + response_len + 1] - if self._calc_checksum(dat) != 0: - raise PandaSpiBadChecksum - return dat[3:-1] + deadline = time.monotonic() + (timeout * 1e-3 if timeout > 0 else 3600.0) + + cnt = 0 + status = 0 + while status != ACK: + cnt = (cnt+1) % 0xff + response = spi.xfer2(cnt.to_bytes() * SPI_BUF_SIZE) # cnt is nice for debugging + status = response[0] + if status == NACK: + raise PandaSpiNackResponse + if timeout != 0 and time.monotonic() > deadline: + raise PandaSpiMissingAck + + len_bytes = response[1:3] + response_len = struct.unpack(' max_rx_len: + raise PandaSpiException(f"response length greater than max ({max_rx_len} {response_len})") + + data_ck = response[3:3+response_len+1] + frame_head = bytes([ACK]) + bytes(len_bytes) + bytes(data_ck) + if self._calc_checksum(frame_head) != 0: + raise PandaSpiBadChecksum + + return bytes(data_ck[:-1]) 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) @@ -193,46 +185,43 @@ def _transfer(self, endpoint: int, data, timeout: int, max_rx_len: int = 1000, e except PandaSpiException as e: exc = e 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 - + if self.no_retry: + break + self._resync(spi) raise exc + def _resync(self, spi): + # ensure slave is in a consistent state and ready for the next transfer + # (e.g. slave isn't stuck trying to RX/TX a massive buffer) + attempts = 5 + while attempts > 0: + x = spi.xfer2([0x00, ]*SPI_BUF_SIZE) + if x[0] == NACK and set(bytes(x[1:])) == {0xcc, }: + break + attempts -= 1 + def get_protocol_version(self) -> bytes: vers_str = b"VERSION" + def _get_version(spi) -> bytes: - spi.writebytes(vers_str) - - logger.debug("- waiting for echo") - start = time.monotonic() - while True: - version_bytes = spi.readbytes(len(vers_str) + 2) - if bytes(version_bytes).startswith(vers_str): - break - if (time.monotonic() - start) > 0.001: - raise PandaSpiMissingAck - - rlen = struct.unpack(" 1000: raise PandaSpiException("response length greater than max") - # get response - dat = spi.readbytes(rlen + 1) - resp = dat[:-1] - calculated_crc = crc8(bytes(version_bytes + resp)) - if calculated_crc != dat[-1]: + data_ck = response[9:9+rlen+1] + if crc8(vers_str + len_bytes + bytes(data_ck[:-1])) != data_ck[-1]: raise PandaSpiBadChecksum - return bytes(resp) + + return bytes(data_ck[:-1]) exc = PandaSpiException() with self.dev.acquire() as spi: @@ -242,6 +231,8 @@ def _get_version(spi) -> bytes: except PandaSpiException as e: exc = e logger.debug("SPI get protocol version failed, retrying", exc_info=True) + self._resync(spi) + raise exc # libusb1 functions diff --git a/scripts/health_test.py b/scripts/health_test.py deleted file mode 100755 index 1195c2d7f21..00000000000 --- a/scripts/health_test.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -import time -from panda import Panda - -if __name__ == "__main__": - i = 0 - pi = 0 - - panda = Panda() - while True: - st = time.monotonic() - while time.monotonic() - st < 1: - panda.health() - i += 1 - print(i, panda.health(), "\n") - print(f"Speed: {i - pi}Hz") - pi = i - diff --git a/scripts/spi_test.py b/scripts/spi_test.py new file mode 100755 index 00000000000..9986ab399b0 --- /dev/null +++ b/scripts/spi_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import os +import sys +import time +from datetime import datetime +from collections import defaultdict, deque + +os.environ['NO_RETRY'] = '1' # no spi retries +from panda import Panda, PandaSpiException + + +if __name__ == "__main__": + s = defaultdict(lambda: deque(maxlen=30)) + def avg(k): + return sum(s[k])/len(s[k]) + + core = 6 + from openpilot.common.realtime import config_realtime_process + #config_realtime_process(core, 10) + #os.system(f"echo {core} | sudo tee /proc/irq/10/smp_affinity_list") + #os.system("sudo chrt -f -p 1 $(pgrep -f spi0)") + #print(f"sudo taskset -pc {core} $(pgrep -f spi0)") + #os.system(f"sudo taskset -pc {core} $(pgrep -f spi0)") + + p = Panda() + p.reset() + start = datetime.now() + le = p.health()['spi_error_count'] + while True: + cnt = 0 + st = time.monotonic() + while time.monotonic() - st < 1: + try: + p.get_type() # get_type has no processing on panda side + except PandaSpiException: + print(f"ERROR after {datetime.now() - start}\n\n") + sys.stdout.write("\033[1;32;40m" + p.serial_read(0).decode('utf8') + "\033[00m") + sys.stdout.flush() + sys.exit() + cnt += 1 + s['hz'].append(cnt) + + err = p.health()['spi_error_count'] + s['err'].append((err - le) & 0xFFFFFFFF) + le = err + + print( + f"{avg('hz'):04.0f}Hz {avg('err'):04.0f} errors [{cnt:04d}Hz {s['err'][-1]:04d} errors] {datetime.now() - start}" + ) + diff --git a/test.py b/test.py new file mode 100644 index 00000000000..d18de27c289 --- /dev/null +++ b/test.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +from panda.python.spi import PandaSpiHandle + + +h = PandaSpiHandle() +with h.dev.acquire() as spi: + for _ in range(10): + print(_, bytes(spi.xfer2([0]*4096)[:10])) diff --git a/tests/hitl/5_spi.py b/tests/hitl/5_spi.py index 106a1c7d4b2..c41819d6614 100644 --- a/tests/hitl/5_spi.py +++ b/tests/hitl/5_spi.py @@ -10,7 +10,7 @@ class TestSpi: def _ping(self, mocker, panda): # should work with no retries - spy = mocker.spy(panda._handle, '_wait_for_ack') + spy = mocker.spy(panda._handle, '_cmd_no_retry') panda.health() assert spy.call_count == 2 mocker.stop(spy) @@ -41,17 +41,17 @@ def test_protocol_version_data(self, p): assert bstub == (0xEE if bootstub else 0xCC) def test_all_comm_types(self, mocker, p): - spy = mocker.spy(p._handle, '_wait_for_ack') + spy = mocker.spy(p._handle, '_cmd_no_retry') # controlRead + controlWrite p.health() p.can_clear(0) - assert spy.call_count == 2*2 + assert spy.call_count == 2 # bulkRead + bulkWrite p.can_recv() p.can_send(0x123, b"somedata", 0) - assert spy.call_count == 2*4 + assert spy.call_count == 4 def test_bad_header(self, mocker, p): with patch('panda.python.spi.SYNC', return_value=0):