diff --git a/Firmware/Board/v3/Src/stm32f4xx_it.c b/Firmware/Board/v3/Src/stm32f4xx_it.c index a61e71d3..1e69bcb9 100644 --- a/Firmware/Board/v3/Src/stm32f4xx_it.c +++ b/Firmware/Board/v3/Src/stm32f4xx_it.c @@ -37,7 +37,7 @@ #include "cmsis_os.h" /* USER CODE BEGIN 0 */ - +#include /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ @@ -67,7 +67,7 @@ extern TIM_HandleTypeDef htim14; void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ - + COUNT_IRQ(NonMaskableInt_IRQn); /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ @@ -110,7 +110,7 @@ void HardFault_Handler(void) void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - + COUNT_IRQ(MemoryManagement_IRQn); /* USER CODE END MemoryManagement_IRQn 0 */ while (1) { @@ -128,7 +128,7 @@ void MemManage_Handler(void) void BusFault_Handler(void) { /* USER CODE BEGIN BusFault_IRQn 0 */ - + COUNT_IRQ(BusFault_IRQn); /* USER CODE END BusFault_IRQn 0 */ while (1) { @@ -146,7 +146,7 @@ void BusFault_Handler(void) void UsageFault_Handler(void) { /* USER CODE BEGIN UsageFault_IRQn 0 */ - + COUNT_IRQ(UsageFault_IRQn); /* USER CODE END UsageFault_IRQn 0 */ while (1) { @@ -164,7 +164,7 @@ void UsageFault_Handler(void) void DebugMon_Handler(void) { /* USER CODE BEGIN DebugMonitor_IRQn 0 */ - + COUNT_IRQ(DebugMonitor_IRQn); /* USER CODE END DebugMonitor_IRQn 0 */ /* USER CODE BEGIN DebugMonitor_IRQn 1 */ @@ -177,7 +177,7 @@ void DebugMon_Handler(void) void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ - + COUNT_IRQ(SysTick_IRQn); /* USER CODE END SysTick_IRQn 0 */ osSystickHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ @@ -198,7 +198,7 @@ void SysTick_Handler(void) void DMA1_Stream0_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream0_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream0_IRQn); /* USER CODE END DMA1_Stream0_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_spi3_rx); /* USER CODE BEGIN DMA1_Stream0_IRQn 1 */ @@ -212,7 +212,7 @@ void DMA1_Stream0_IRQHandler(void) void DMA1_Stream2_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream2_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream2_IRQn); /* USER CODE END DMA1_Stream2_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_uart4_rx); /* USER CODE BEGIN DMA1_Stream2_IRQn 1 */ @@ -226,7 +226,7 @@ void DMA1_Stream2_IRQHandler(void) void DMA1_Stream4_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream4_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream4_IRQn); /* USER CODE END DMA1_Stream4_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_uart4_tx); /* USER CODE BEGIN DMA1_Stream4_IRQn 1 */ @@ -240,7 +240,7 @@ void DMA1_Stream4_IRQHandler(void) void DMA1_Stream5_IRQHandler(void) { /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */ - + COUNT_IRQ(DMA1_Stream5_IRQn); /* USER CODE END DMA1_Stream5_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_spi3_tx); /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */ @@ -254,7 +254,7 @@ void DMA1_Stream5_IRQHandler(void) void CAN1_TX_IRQHandler(void) { /* USER CODE BEGIN CAN1_TX_IRQn 0 */ - + COUNT_IRQ(CAN1_TX_IRQn); /* USER CODE END CAN1_TX_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_TX_IRQn 1 */ @@ -268,7 +268,7 @@ void CAN1_TX_IRQHandler(void) void CAN1_RX0_IRQHandler(void) { /* USER CODE BEGIN CAN1_RX0_IRQn 0 */ - + COUNT_IRQ(CAN1_RX0_IRQn); /* USER CODE END CAN1_RX0_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_RX0_IRQn 1 */ @@ -282,7 +282,7 @@ void CAN1_RX0_IRQHandler(void) void CAN1_RX1_IRQHandler(void) { /* USER CODE BEGIN CAN1_RX1_IRQn 0 */ - + COUNT_IRQ(CAN1_RX1_IRQn); /* USER CODE END CAN1_RX1_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_RX1_IRQn 1 */ @@ -296,7 +296,7 @@ void CAN1_RX1_IRQHandler(void) void CAN1_SCE_IRQHandler(void) { /* USER CODE BEGIN CAN1_SCE_IRQn 0 */ - + COUNT_IRQ(CAN1_SCE_IRQn); /* USER CODE END CAN1_SCE_IRQn 0 */ HAL_CAN_IRQHandler(&hcan1); /* USER CODE BEGIN CAN1_SCE_IRQn 1 */ @@ -310,7 +310,7 @@ void CAN1_SCE_IRQHandler(void) void TIM8_TRG_COM_TIM14_IRQHandler(void) { /* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 0 */ - + COUNT_IRQ(TIM8_TRG_COM_TIM14_IRQn); /* USER CODE END TIM8_TRG_COM_TIM14_IRQn 0 */ HAL_TIM_IRQHandler(&htim8); HAL_TIM_IRQHandler(&htim14); @@ -325,7 +325,7 @@ void TIM8_TRG_COM_TIM14_IRQHandler(void) void SPI3_IRQHandler(void) { /* USER CODE BEGIN SPI3_IRQn 0 */ - + COUNT_IRQ(SPI3_IRQn); /* USER CODE END SPI3_IRQn 0 */ HAL_SPI_IRQHandler(&hspi3); /* USER CODE BEGIN SPI3_IRQn 1 */ @@ -339,7 +339,7 @@ void SPI3_IRQHandler(void) void UART4_IRQHandler(void) { /* USER CODE BEGIN UART4_IRQn 0 */ - + COUNT_IRQ(UART4_IRQn); /* USER CODE END UART4_IRQn 0 */ HAL_UART_IRQHandler(&huart4); /* USER CODE BEGIN UART4_IRQn 1 */ diff --git a/Firmware/Board/v3/board.cpp b/Firmware/Board/v3/board.cpp index 429ebd84..c1fd3015 100644 --- a/Firmware/Board/v3/board.cpp +++ b/Firmware/Board/v3/board.cpp @@ -355,16 +355,19 @@ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { void TIM1_UP_TIM10_IRQHandler(void) { + COUNT_IRQ(TIM1_UP_TIM10_IRQn); __HAL_TIM_CLEAR_IT(&htim1, TIM_IT_UPDATE); motors[0].tim_update_cb(); } void TIM8_UP_TIM13_IRQHandler(void) { + COUNT_IRQ(TIM8_UP_TIM13_IRQn); __HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE); motors[1].tim_update_cb(); } void TIM5_IRQHandler(void) { + COUNT_IRQ(TIM5_IRQn); pwm0_input.on_capture(); } @@ -386,6 +389,8 @@ void ADC_IRQ_Dispatch(ADC_HandleTypeDef* hadc, void(*callback)(ADC_HandleTypeDef } void ADC_IRQHandler(void) { + COUNT_IRQ(ADC_IRQn); + // The HAL's ADC handling mechanism adds many clock cycles of overhead // So we bypass it and handle the logic ourselves. //@TODO add vbus measurement on adc1 here @@ -395,14 +400,17 @@ void ADC_IRQHandler(void) { } void I2C1_EV_IRQHandler(void) { - HAL_I2C_EV_IRQHandler(&hi2c1); + COUNT_IRQ(I2C1_EV_IRQn); + HAL_I2C_EV_IRQHandler(&hi2c1); } void I2C1_ER_IRQHandler(void) { - HAL_I2C_ER_IRQHandler(&hi2c1); + COUNT_IRQ(I2C1_ER_IRQn); + HAL_I2C_ER_IRQHandler(&hi2c1); } void OTG_FS_IRQHandler(void) { + COUNT_IRQ(OTG_FS_IRQn); // Mask interrupt, and signal processing of interrupt by usb_cmd_thread // The thread will re-enable the interrupt when all pending irqs are clear. HAL_NVIC_DisableIRQ(OTG_FS_IRQn); diff --git a/Firmware/Drivers/STM32/stm32_system.cpp b/Firmware/Drivers/STM32/stm32_system.cpp new file mode 100644 index 00000000..078cc058 --- /dev/null +++ b/Firmware/Drivers/STM32/stm32_system.cpp @@ -0,0 +1,4 @@ + +#include "stm32_system.h" + +uint32_t irq_counters[254]; // 14 core interrupts, 240 NVIC interrupts diff --git a/Firmware/Drivers/STM32/stm32_system.h b/Firmware/Drivers/STM32/stm32_system.h index ce8cae32..5a95e590 100644 --- a/Firmware/Drivers/STM32/stm32_system.h +++ b/Firmware/Drivers/STM32/stm32_system.h @@ -13,6 +13,19 @@ extern "C" { #endif +// Uncomment the following line to sacrifice 1kB of RAM for the ability to +// monitor the number of times each interrupt fires. +//#define ENABLE_IRQ_COUNTER + +#ifdef ENABLE_IRQ_COUNTER +extern uint32_t irq_counters[]; +#define COUNT_IRQ(irqn) (++irq_counters[irqn + 14]) +#define GET_IRQ_COUNTER(irqn) irq_counters[irqn + 14] +#else +#define COUNT_IRQ(irqn) ((void)0) +#define GET_IRQ_COUNTER(irqn) 0 +#endif + static inline uint32_t cpu_enter_critical() { uint32_t primask = __get_PRIMASK(); __disable_irq(); diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index d3a5ec6b..646d3d46 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -180,6 +180,22 @@ public: Axis& get_axis(int num) { return axes[num]; } ODriveCAN& get_can() { return *odCAN; } + uint32_t get_interrupt_status(int32_t irqn) { + if ((irqn < -14) || (irqn >= 240)) { + return 0xffffffff; + } + + uint8_t priority = (irqn < -12) + ? 0 // hard fault and NMI always have maximum priority + : NVIC_GetPriority((IRQn_Type)irqn); + uint32_t counter = GET_IRQ_COUNTER((IRQn_Type)irqn); + bool is_enabled = (irqn < 0) + ? true // processor interrupt vectors are always enabled + : NVIC->ISER[(((uint32_t)(int32_t)irqn) >> 5UL)] & (uint32_t)(1UL << (((uint32_t)(int32_t)irqn) & 0x1FUL)); + + return priority | ((counter & 0x7ffffff) << 8) | (is_enabled ? 0x80000000 : 0); + } + float& vbus_voltage_ = ::vbus_voltage; // TODO: make this the actual variable float& ibus_ = ::ibus_; // TODO: make this the actual variable float ibus_report_filter_k_ = 1.0f; diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 7c787e71..33b99167 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -196,6 +196,7 @@ sources = { 'MotorControl/trapTraj.cpp', 'MotorControl/pwm_input.cpp', 'MotorControl/main.cpp', + 'Drivers/STM32/stm32_system.cpp', 'Drivers/STM32/stm32_gpio.cpp', 'Drivers/STM32/stm32_nvm.c', 'Drivers/STM32/stm32_spi_arbiter.cpp', diff --git a/Firmware/odrive-interface.yaml b/Firmware/odrive-interface.yaml index 37ec69e0..7b7e3695 100644 --- a/Firmware/odrive-interface.yaml +++ b/Firmware/odrive-interface.yaml @@ -241,6 +241,17 @@ interfaces: erase_configuration: reboot: enter_dfu_mode: + get_interrupt_status: + in: {irqn: {type: int32, doc: '-12...-1: processor interrupts, 0...239: NVIC interrupts'}} + out: + status: + type: uint32 + doc: | + bit 31: enabled (1) or disabled (0) + bits 30:8: number of times the interrupt fired (modulo 0x800000) + bits 7:0: priority (0 is highest priority) + 0xffffffff if the specified number is not a valid interrupt number. + doc: Returns information about the specified interrupt number. ODrive.Can: c_is_class: True diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index 9de5e626..0fdf9f5f 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -5,7 +5,7 @@ import threading import fibre import odrive import odrive.enums -from odrive.utils import calculate_thermistor_coeffs, set_motor_thermistor_coeffs, start_liveplotter, dump_errors, oscilloscope_dump, BulkCapture, step_and_plot +from odrive.utils import calculate_thermistor_coeffs, set_motor_thermistor_coeffs, start_liveplotter, dump_errors, dump_interrupts, oscilloscope_dump, BulkCapture, step_and_plot def print_banner(): print("Website: https://odriverobotics.com/") @@ -85,6 +85,7 @@ def launch_shell(args, logger, app_shutdown_token): 'start_liveplotter': start_liveplotter, 'dump_errors': dump_errors, 'oscilloscope_dump': oscilloscope_dump, + 'dump_interrupts': dump_interrupts, 'BulkCapture': BulkCapture, 'step_and_plot': step_and_plot, 'calculate_thermistor_coeffs': calculate_thermistor_coeffs, diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index dd51f5ac..bd448079 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -361,3 +361,113 @@ def yes_no_prompt(question, default=None): return False elif choice == '' and default is not None: return default + +def dump_interrupts(odrv): + interrupts = [ + (-12, "MemoryManagement_IRQn"), + (-11, "BusFault_IRQn"), + (-10, "UsageFault_IRQn"), + (-5, "SVCall_IRQn"), + (-4, "DebugMonitor_IRQn"), + (-2, "PendSV_IRQn"), + (-1, "SysTick_IRQn"), + (0, "WWDG_IRQn"), + (1, "PVD_IRQn"), + (2, "TAMP_STAMP_IRQn"), + (3, "RTC_WKUP_IRQn"), + (4, "FLASH_IRQn"), + (5, "RCC_IRQn"), + (6, "EXTI0_IRQn"), + (7, "EXTI1_IRQn"), + (8, "EXTI2_IRQn"), + (9, "EXTI3_IRQn"), + (10, "EXTI4_IRQn"), + (11, "DMA1_Stream0_IRQn"), + (12, "DMA1_Stream1_IRQn"), + (13, "DMA1_Stream2_IRQn"), + (14, "DMA1_Stream3_IRQn"), + (15, "DMA1_Stream4_IRQn"), + (16, "DMA1_Stream5_IRQn"), + (17, "DMA1_Stream6_IRQn"), + (18, "ADC_IRQn"), + (19, "CAN1_TX_IRQn"), + (20, "CAN1_RX0_IRQn"), + (21, "CAN1_RX1_IRQn"), + (22, "CAN1_SCE_IRQn"), + (23, "EXTI9_5_IRQn"), + (24, "TIM1_BRK_TIM9_IRQn"), + (25, "TIM1_UP_TIM10_IRQn"), + (26, "TIM1_TRG_COM_TIM11_IRQn"), + (27, "TIM1_CC_IRQn"), + (28, "TIM2_IRQn"), + (29, "TIM3_IRQn"), + (30, "TIM4_IRQn"), + (31, "I2C1_EV_IRQn"), + (32, "I2C1_ER_IRQn"), + (33, "I2C2_EV_IRQn"), + (34, "I2C2_ER_IRQn"), + (35, "SPI1_IRQn"), + (36, "SPI2_IRQn"), + (37, "USART1_IRQn"), + (38, "USART2_IRQn"), + (39, "USART3_IRQn"), + (40, "EXTI15_10_IRQn"), + (41, "RTC_Alarm_IRQn"), + (42, "OTG_FS_WKUP_IRQn"), + (43, "TIM8_BRK_TIM12_IRQn"), + (44, "TIM8_UP_TIM13_IRQn"), + (45, "TIM8_TRG_COM_TIM14_IRQn"), + (46, "TIM8_CC_IRQn"), + (47, "DMA1_Stream7_IRQn"), + (48, "FMC_IRQn"), + (49, "SDMMC1_IRQn"), + (50, "TIM5_IRQn"), + (51, "SPI3_IRQn"), + (52, "UART4_IRQn"), + (53, "UART5_IRQn"), + (54, "TIM6_DAC_IRQn"), + (55, "TIM7_IRQn"), + (56, "DMA2_Stream0_IRQn"), + (57, "DMA2_Stream1_IRQn"), + (58, "DMA2_Stream2_IRQn"), + (59, "DMA2_Stream3_IRQn"), + (60, "DMA2_Stream4_IRQn"), + (61, "ETH_IRQn"), + (62, "ETH_WKUP_IRQn"), + (63, "CAN2_TX_IRQn"), + (64, "CAN2_RX0_IRQn"), + (65, "CAN2_RX1_IRQn"), + (66, "CAN2_SCE_IRQn"), + (67, "OTG_FS_IRQn"), + (68, "DMA2_Stream5_IRQn"), + (69, "DMA2_Stream6_IRQn"), + (70, "DMA2_Stream7_IRQn"), + (71, "USART6_IRQn"), + (72, "I2C3_EV_IRQn"), + (73, "I2C3_ER_IRQn"), + (74, "OTG_HS_EP1_OUT_IRQn"), + (75, "OTG_HS_EP1_IN_IRQn"), + (76, "OTG_HS_WKUP_IRQn"), + (77, "OTG_HS_IRQn"), + # gap + (80, "RNG_IRQn"), + (81, "FPU_IRQn"), + (82, "UART7_IRQn"), + (83, "UART8_IRQn"), + (84, "SPI4_IRQn"), + (85, "SPI5_IRQn"), + # gap + (87, "SAI1_IRQn"), + # gap + (91, "SAI2_IRQn"), + (92, "QUADSPI_IRQn"), + (93, "LPTIM1_IRQn"), + # gap + (103, "SDMMC2_IRQn") + ] + + print(" # Name En Prio Count") + for irqn, irq_name in interrupts: + status = odrv.get_interrupt_status(irqn) + if (status != 0): + print(str(irqn).rjust(3) + " " + irq_name.ljust(23) + " " + str("*" if (status & 0x80000000) else " ") + " " + str(status & 0xff).rjust(4) + " " + str((status >> 8) & 0x7fffff).rjust(7))