From e7131fcee0e68e9d6751aa5baf83b5898ab007bf Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 14 Nov 2022 13:52:52 -0800 Subject: [PATCH] H7 SPI comms (#1158) * getting health packets * start cleanup * pull state machine out * cleanup --- board/boards/tres.h | 7 ++ board/drivers/spi.h | 141 ++++++++++++++++++++++++++++++++++-- board/stm32fx/llspi.h | 106 ++------------------------- board/stm32h7/llspi.h | 90 ++++++++++++++++++++++- board/stm32h7/peripherals.h | 3 + 5 files changed, 238 insertions(+), 109 deletions(-) diff --git a/board/boards/tres.h b/board/boards/tres.h index d31f53a1..095783a8 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -9,6 +9,13 @@ void tres_init(void) { while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0); red_chiplet_init(); + + // SPI init + set_gpio_alternate(GPIOE, 11, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 12, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 13, GPIO_AF5_SPI4); + set_gpio_alternate(GPIOE, 14, GPIO_AF5_SPI4); + register_set_bits(&(GPIOE->OSPEEDR), GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12 | GPIO_OSPEEDR_OSPEED13 | GPIO_OSPEEDR_OSPEED14); } const board board_tres = { diff --git a/board/drivers/spi.h b/board/drivers/spi.h index ae91de49..42267855 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -1,8 +1,14 @@ #pragma once #define SPI_BUF_SIZE 1024U + +#ifdef STM32H7 +__attribute__((section(".ram_d1"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; +__attribute__((section(".ram_d1"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; +#else uint8_t spi_buf_rx[SPI_BUF_SIZE]; uint8_t spi_buf_tx[SPI_BUF_SIZE]; +#endif #define SPI_CHECKSUM_START 0xABU #define SPI_SYNC_BYTE 0x5AU @@ -12,21 +18,39 @@ uint8_t spi_buf_tx[SPI_BUF_SIZE]; // SPI states enum { - SPI_RX_STATE_HEADER=0U, - SPI_RX_STATE_HEADER_ACK=1U, - SPI_RX_STATE_HEADER_NACK=2U, - SPI_RX_STATE_DATA_RX=3U, - SPI_RX_STATE_DATA_RX_ACK=4U, - SPI_RX_STATE_DATA_TX=5U + SPI_STATE_HEADER, + SPI_STATE_HEADER_ACK, + SPI_STATE_HEADER_NACK, + SPI_STATE_DATA_RX, + SPI_STATE_DATA_RX_ACK, + SPI_STATE_DATA_TX }; -uint8_t spi_state = SPI_RX_STATE_HEADER; +uint8_t spi_state = SPI_STATE_HEADER; uint8_t spi_endpoint; uint16_t spi_data_len_mosi; uint16_t spi_data_len_miso; #define SPI_HEADER_SIZE 7U +// low level SPI prototypes +void llspi_init(void); +void llspi_mosi_dma(uint8_t *addr, int len); +void llspi_miso_dma(uint8_t *addr, int len); + +void spi_init(void) { + // clear buffers (for debugging) + (void)memset(spi_buf_rx, 0, SPI_BUF_SIZE); + (void)memset(spi_buf_tx, 0, SPI_BUF_SIZE); + + // platform init + llspi_init(); + + // Start the first packet! + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); +} + bool check_checksum(uint8_t *data, uint16_t len) { // TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards uint8_t checksum = SPI_CHECKSUM_START; @@ -35,3 +59,106 @@ bool check_checksum(uint8_t *data, uint16_t len) { } return checksum == 0U; } + +void spi_handle_rx(void) { + uint8_t next_rx_state = SPI_STATE_HEADER; + + // 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 (spi_state == SPI_STATE_HEADER) { + if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && check_checksum(spi_buf_rx, SPI_HEADER_SIZE)) { + // response: ACK and start receiving data portion + spi_buf_tx[0] = SPI_HACK; + next_rx_state = SPI_STATE_HEADER_ACK; + } else { + // response: NACK and reset state machine + puts("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); + spi_buf_tx[0] = SPI_NACK; + next_rx_state = SPI_STATE_HEADER_NACK; + } + llspi_miso_dma(spi_buf_tx, 1); + } else if (spi_state == SPI_STATE_DATA_RX) { + // We got everything! Based on the endpoint specified, call the appropriate handler + uint16_t response_len = 0U; + bool reponse_ack = false; + if (check_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U)) { + if (spi_endpoint == 0U) { + if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { + ControlPacket_t ctrl; + (void)memcpy(&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t)); + response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]); + reponse_ack = true; + } else { + puts("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); + reponse_ack = true; + } else { + puts("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); + reponse_ack = true; + } else if (spi_endpoint == 3U) { + if (spi_data_len_mosi > 0U) { + comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi); + reponse_ack = true; + } else { + puts("SPI: did expect data for can_write\n"); + } + } else { + puts("SPI: unexpected endpoint"); puth(spi_endpoint); puts("\n"); + } + } else { + // Checksum was incorrect + reponse_ack = false; + puts("- incorrect data checksum\n"); + } + + // Setup response header + spi_buf_tx[0] = reponse_ack ? SPI_DACK : SPI_NACK; + spi_buf_tx[1] = response_len & 0xFFU; + spi_buf_tx[2] = (response_len >> 8) & 0xFFU; + + // Add checksum + uint8_t checksum = SPI_CHECKSUM_START; + for(uint16_t i = 0U; i < (response_len + 3U); i++) { + checksum ^= spi_buf_tx[i]; + } + spi_buf_tx[response_len + 3U] = checksum; + + // Write response + llspi_miso_dma(spi_buf_tx, response_len + 4U); + + next_rx_state = SPI_STATE_DATA_TX; + } else { + puts("SPI: RX unexpected state: "); puth(spi_state); puts("\n"); + } + + spi_state = next_rx_state; +} + +void spi_handle_tx(void) { + 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_HEADER_NACK) { + // Reset state + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); + } 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); + puts("SPI: TX unexpected state: "); puth(spi_state); puts("\n"); + } +} diff --git a/board/stm32fx/llspi.h b/board/stm32fx/llspi.h index 51bde6a2..020ead50 100644 --- a/board/stm32fx/llspi.h +++ b/board/stm32fx/llspi.h @@ -1,4 +1,4 @@ -void spi_miso_dma(uint8_t *addr, int len) { +void llspi_miso_dma(uint8_t *addr, int len) { // disable DMA DMA2_Stream3->CR &= ~DMA_SxCR_EN; register_clear_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN); @@ -12,7 +12,7 @@ void spi_miso_dma(uint8_t *addr, int len) { DMA2_Stream3->CR |= DMA_SxCR_EN; } -void spi_mosi_dma(uint8_t *addr, int len) { +void llspi_mosi_dma(uint8_t *addr, int len) { // disable DMA register_clear_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN); DMA2_Stream2->CR &= ~DMA_SxCR_EN; @@ -36,82 +36,8 @@ void DMA2_Stream2_IRQ_Handler(void) { ENTER_CRITICAL(); DMA2->LIFCR = DMA_LIFCR_CTCIF2; - uint8_t next_rx_state = SPI_RX_STATE_HEADER; + spi_handle_rx(); - // 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 (spi_state == SPI_RX_STATE_HEADER) { - if (spi_buf_rx[0] == SPI_SYNC_BYTE && check_checksum(spi_buf_rx, SPI_HEADER_SIZE)) { - // response: ACK and start receiving data portion - spi_buf_tx[0] = SPI_HACK; - next_rx_state = SPI_RX_STATE_HEADER_ACK; - } else { - // response: NACK and reset state machine - puts("SPI: incorrect header sync or checksum "); /*hexdump(spi_buf_rx, SPI_HEADER_SIZE);*/ - spi_buf_tx[0] = SPI_NACK; - next_rx_state = SPI_RX_STATE_HEADER_NACK; - } - spi_miso_dma(spi_buf_tx, 1); - } else if (spi_state == SPI_RX_STATE_DATA_RX) { - // We got everything! Based on the endpoint specified, call the appropriate handler - uint16_t response_len = 0U; - bool reponse_ack = false; - if (check_checksum(spi_buf_rx + SPI_HEADER_SIZE, spi_data_len_mosi + 1)) { - if (spi_endpoint == 0U) { - if (spi_data_len_mosi >= sizeof(ControlPacket_t)) { - response_len = comms_control_handler((ControlPacket_t *)(spi_buf_rx + SPI_HEADER_SIZE), spi_buf_tx + 3); - reponse_ack = true; - } else { - puts("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); - reponse_ack = true; - } else { - puts("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); - reponse_ack = true; - } else if (spi_endpoint == 3U) { - if (spi_data_len_mosi > 0U) { - comms_can_write(spi_buf_rx + SPI_HEADER_SIZE, spi_data_len_mosi); - reponse_ack = true; - } else { - puts("SPI: did expect data for can_write\n"); - } - } - } else { - // Checksum was incorrect - reponse_ack = false; - puts("SPI: incorrect data checksum\n"); - } - - // Setup response header - spi_buf_tx[0] = reponse_ack ? SPI_DACK : SPI_NACK; - spi_buf_tx[1] = response_len & 0xFFU; - spi_buf_tx[2] = (response_len >> 8) & 0xFFU; - - // Add checksum - uint8_t checksum = SPI_CHECKSUM_START; - for(uint16_t i = 0U; i < response_len + 3; i++) { - checksum ^= spi_buf_tx[i]; - } - spi_buf_tx[response_len + 3] = checksum; - - // Write response - spi_miso_dma(spi_buf_tx, response_len + 4); - - next_rx_state = SPI_RX_STATE_DATA_TX; - } else { - puts("SPI: RX unexpected state: "); puth(spi_state); puts("\n"); - } - - spi_state = next_rx_state; EXIT_CRITICAL(); } @@ -127,33 +53,15 @@ void DMA2_Stream3_IRQ_Handler(void) { (void)dat; SPI1->DR = 0U; - if (spi_state == SPI_RX_STATE_HEADER_ACK) { - // ACK was sent, queue up the RX buf for the data + checksum - spi_state = SPI_RX_STATE_DATA_RX; - spi_mosi_dma(spi_buf_rx + SPI_HEADER_SIZE, spi_data_len_mosi + 1); - } else if (spi_state == SPI_RX_STATE_HEADER_NACK) { - // Reset state - spi_state = SPI_RX_STATE_HEADER; - spi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else if (spi_state == SPI_RX_STATE_DATA_TX) { - // Reset state - spi_state = SPI_RX_STATE_HEADER; - spi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - } else { - puts("SPI: TX unexpected state: "); puth(spi_state); puts("\n"); - } + spi_handle_tx(); } // ***************************** SPI init ***************************** -void spi_init(void) { +void llspi_init(void) { // We expect less than 50 transactions (including control messages and CAN buffers) at the 100Hz boardd interval. Can be raised if needed. REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, 5000U, FAULT_INTERRUPT_RATE_SPI_DMA) REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, 5000U, FAULT_INTERRUPT_RATE_SPI_DMA) - // Clear buffers (for debugging) - memset(spi_buf_rx, 0, SPI_BUF_SIZE); - memset(spi_buf_tx, 0, SPI_BUF_SIZE); - // Setup MOSI DMA register_set(&(DMA2_Stream2->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_TCIE), 0x1E077EFEU); register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU); @@ -167,10 +75,6 @@ void spi_init(void) { register_set(&(SPI1->CR1), SPI_CR1_SPE, 0xFFFFU); register_set(&(SPI1->CR2), 0U, 0xF7U); - // Start the first packet! - spi_state = SPI_RX_STATE_HEADER; - spi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); - NVIC_EnableIRQ(DMA2_Stream2_IRQn); NVIC_EnableIRQ(DMA2_Stream3_IRQn); } diff --git a/board/stm32h7/llspi.h b/board/stm32h7/llspi.h index f9973b49..0215c6fb 100644 --- a/board/stm32h7/llspi.h +++ b/board/stm32h7/llspi.h @@ -1,3 +1,91 @@ -void spi_init(void) { +// master -> panda DMA start +void llspi_mosi_dma(uint8_t *addr, int len) { + // 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); + // drain the bus + while ((SPI4->SR & SPI_SR_RXP) != 0U) { + volatile uint8_t dat = SPI4->RXDR; + (void)dat; + } + + // setup destination and length + register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream2->NDTR = len; + + // 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); +} + +// panda -> master DMA start +void llspi_miso_dma(uint8_t *addr, int len) { + // disable DMA + DMA2_Stream3->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + + // setup source and length + register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream3->NDTR = len; + + // clear under-run while we were reading + SPI4->IFCR |= SPI_IFCR_UDRC; + + // enable DMA + register_set_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + DMA2_Stream3->CR |= DMA_SxCR_EN; +} + +// master -> panda DMA finished +void DMA2_Stream2_IRQ_Handler(void) { + // Clear interrupt flag + ENTER_CRITICAL(); + DMA2->LIFCR = DMA_LIFCR_CTCIF2; + + spi_handle_rx(); + + EXIT_CRITICAL(); +} + +// panda -> master DMA finished +void DMA2_Stream3_IRQ_Handler(void) { + // Clear interrupt flag + DMA2->LIFCR = DMA_LIFCR_CTCIF3; + + // Wait until the transaction is actually finished and clear the DR + // TODO: needs a timeout here, otherwise it gets stuck with no master clock! + while (!(SPI4->SR & SPI_SR_TXC)); + volatile uint8_t dat = SPI4->TXDR; + (void)dat; + + spi_handle_tx(); +} + + +void llspi_init(void) { + // We expect less than 50 transactions (including control messages and CAN buffers) at the 100Hz boardd interval. Can be raised if needed. + REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, 5000U, FAULT_INTERRUPT_RATE_SPI_DMA) + REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, 5000U, FAULT_INTERRUPT_RATE_SPI_DMA) + + // Setup MOSI DMA + register_set(&(DMAMUX1_Channel10->CCR), 83U, 0xFFFFFFFFU); + register_set(&(DMA2_Stream2->CR), (DMA_SxCR_MINC | DMA_SxCR_TCIE), 0x1E077EFEU); + register_set(&(DMA2_Stream2->PAR), (uint32_t)&(SPI4->RXDR), 0xFFFFFFFFU); + + // Setup MISO DMA, memory -> peripheral + register_set(&(DMAMUX1_Channel11->CCR), 84U, 0xFFFFFFFFU); + register_set(&(DMA2_Stream3->CR), (DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE), 0x1E077EFEU); + register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI4->TXDR), 0xFFFFFFFFU); + + // Enable SPI + 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->CR1), SPI_CR1_SPE, 0xFFFFU); + register_set(&(SPI4->CR2), 0, 0xFFFFU); + + NVIC_EnableIRQ(DMA2_Stream2_IRQn); + NVIC_EnableIRQ(DMA2_Stream3_IRQn); } diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index a8848769..cc52e608 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -85,6 +85,7 @@ void common_init_gpio(void) { void flasher_peripherals_init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; } // Peripheral initialization @@ -98,6 +99,8 @@ void peripherals_init(void) { RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // SPI DMA RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter RCC->APB1LENR |= RCC_APB1LENR_TIM6EN; // interrupt timer RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // clock source timer