From 03af7b610716bf413b11754f4de19e27a52b7f37 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Apr 2024 03:19:19 -0500 Subject: [PATCH] wip --- panda/.github/workflows/drivers.yaml | 23 + panda/.github/workflows/repo.yml | 28 + panda/.github/workflows/test.yaml | 119 + panda/.pre-commit-config.yaml | 20 + panda/Dockerfile | 91 + panda/Jenkinsfile | 156 + panda/LICENSE | 7 + panda/README.md | 104 + panda/SConscript | 188 + panda/SConstruct | 28 + panda/board/SConscript | 21 + panda/board/boards/black.h | 186 + panda/board/boards/board_declarations.h | 77 + panda/board/boards/cuatro.h | 129 + panda/board/boards/dos.h | 215 + panda/board/boards/grey.h | 30 + panda/board/boards/red.h | 198 + panda/board/boards/red_chiplet.h | 152 + panda/board/boards/tres.h | 96 + panda/board/boards/uno.h | 222 + panda/board/boards/unused_funcs.h | 26 + panda/board/boards/white.h | 249 + panda/board/bootstub_declarations.h | 20 + panda/board/can_comms.h | 122 + panda/board/can_definitions.h | 34 + panda/board/comms_definitions.h | 12 + panda/board/config.h | 45 + panda/board/crc.h | 19 + panda/board/critical.h | 23 + panda/board/drivers/bootkick.h | 67 + panda/board/drivers/bxcan.h | 213 + panda/board/drivers/can_common.h | 295 + panda/board/drivers/clock_source.h | 37 + panda/board/drivers/fake_siren.h | 76 + panda/board/drivers/fan.h | 95 + panda/board/drivers/fdcan.h | 267 + panda/board/drivers/gmlan_alt.h | 270 + panda/board/drivers/gpio.h | 92 + panda/board/drivers/harness.h | 135 + panda/board/drivers/interrupts.h | 99 + panda/board/drivers/pwm.h | 56 + panda/board/drivers/registers.h | 81 + panda/board/drivers/simple_watchdog.h | 26 + panda/board/drivers/spi.h | 258 + panda/board/drivers/timers.h | 31 + panda/board/drivers/uart.h | 196 + panda/board/drivers/usb.h | 943 + panda/board/drivers/watchdog.h | 24 + panda/board/early_init.h | 60 + panda/board/fake_stm.h | 33 + panda/board/faults.h | 59 + panda/board/flasher.h | 150 + panda/board/health.h | 62 + panda/board/jungle/SConscript | 18 + panda/board/jungle/__init__.py | 3 +- .../board/jungle/boards/board_declarations.h | 82 + panda/board/jungle/boards/board_v1.h | 178 + panda/board/jungle/boards/board_v2.h | 328 + panda/board/jungle/jungle_health.h | 24 + panda/board/jungle/main_comms.h | 261 + .../jungle/obj/bootstub.panda_jungle.bin | Bin 11072 -> 0 bytes .../jungle/obj/bootstub.panda_jungle.elf | Bin 220372 -> 0 bytes .../jungle/obj/bootstub.panda_jungle_h7.bin | Bin 13492 -> 0 bytes .../jungle/obj/bootstub.panda_jungle_h7.elf | Bin 232908 -> 0 bytes panda/board/jungle/obj/panda_jungle.bin | Bin 55096 -> 0 bytes .../board/jungle/obj/panda_jungle.bin.signed | Bin 55232 -> 0 bytes panda/board/jungle/obj/panda_jungle.elf | Bin 337984 -> 0 bytes panda/board/jungle/obj/panda_jungle_h7.bin | Bin 64148 -> 0 bytes .../jungle/obj/panda_jungle_h7.bin.signed | Bin 64284 -> 0 bytes panda/board/jungle/obj/panda_jungle_h7.elf | Bin 436128 -> 0 bytes panda/board/jungle/scripts/get_version.py | 2 +- panda/board/jungle/stm32f4/board.h | 7 + panda/board/jungle/stm32h7/board.h | 9 + panda/board/jungle/stm32h7/lladc.h | 54 + panda/board/libc.h | 67 + panda/board/main.c | 22 - panda/board/main_comms.h | 397 + panda/board/main_declarations.h | 32 + panda/board/obj/bootstub.panda.bin | Bin 15716 -> 0 bytes panda/board/obj/bootstub.panda.elf | Bin 248692 -> 0 bytes panda/board/obj/bootstub.panda_h7.bin | Bin 17580 -> 0 bytes panda/board/obj/bootstub.panda_h7.elf | Bin 259856 -> 0 bytes panda/board/obj/panda.bin | Bin 62684 -> 0 bytes panda/board/obj/panda.bin.signed | Bin 62820 -> 0 bytes panda/board/obj/panda.elf | Bin 377424 -> 0 bytes panda/board/obj/panda_h7.bin | Bin 71092 -> 0 bytes panda/board/obj/panda_h7.bin.signed | Bin 71228 -> 0 bytes panda/board/obj/panda_h7.elf | Bin 472780 -> 0 bytes panda/board/obj/version | 1 - panda/board/power_saving.h | 46 + panda/board/provision.h | 13 + panda/board/safety.h | 708 + panda/board/safety/safety_body.h | 46 + panda/board/safety/safety_chrysler.h | 296 + panda/board/safety/safety_defaults.h | 68 + panda/board/safety/safety_elm327.h | 37 + panda/board/safety/safety_ford.h | 424 + panda/board/safety/safety_gm.h | 353 + panda/board/safety/safety_honda.h | 526 + panda/board/safety/safety_hyundai.h | 356 + panda/board/safety/safety_hyundai_canfd.h | 360 + panda/board/safety/safety_hyundai_common.h | 118 + panda/board/safety/safety_mazda.h | 130 + panda/board/safety/safety_nissan.h | 166 + panda/board/safety/safety_subaru.h | 295 + panda/board/safety/safety_subaru_preglobal.h | 127 + panda/board/safety/safety_tesla.h | 249 + panda/board/safety/safety_toyota.h | 434 + panda/board/safety/safety_volkswagen_common.h | 10 + panda/board/safety/safety_volkswagen_mqb.h | 312 + panda/board/safety/safety_volkswagen_pq.h | 269 + panda/board/safety_declarations.h | 276 + panda/board/stm32f4/board.h | 39 + panda/board/stm32f4/clock.h | 34 + panda/board/stm32f4/inc/cmsis_compiler.h | 284 + panda/board/stm32f4/inc/cmsis_gcc.h | 2169 ++ panda/board/stm32f4/inc/cmsis_version.h | 40 + panda/board/stm32f4/inc/core_cm3.h | 1938 ++ panda/board/stm32f4/inc/core_cm4.h | 2125 ++ panda/board/stm32f4/inc/mpu_armv7.h | 273 + panda/board/stm32f4/inc/stm32f413xx.h | 14995 ++++++++++ panda/board/stm32f4/inc/stm32f4xx.h | 271 + panda/board/stm32f4/inc/stm32f4xx_hal_def.h | 214 + .../board/stm32f4/inc/stm32f4xx_hal_gpio_ex.h | 1591 + panda/board/stm32f4/inc/system_stm32f4xx.h | 124 + panda/board/stm32f4/interrupt_handlers.h | 98 + panda/board/stm32f4/lladc.h | 24 + panda/board/stm32f4/llbxcan.h | 158 + panda/board/stm32f4/llexti.h | 56 + panda/board/stm32f4/llfan.h | 23 + panda/board/stm32f4/llflash.h | 28 + panda/board/stm32f4/llspi.h | 90 + panda/board/stm32f4/lluart.h | 92 + panda/board/stm32f4/llusb.h | 91 + panda/board/stm32f4/peripherals.h | 91 + .../startup_stm32f413xx.s | 0 panda/board/stm32f4/stm32f4_config.h | 86 + .../{stm32fx => stm32f4}/stm32f4_flash.ld | 0 panda/board/stm32h7/board.h | 53 + panda/board/stm32h7/clock.h | 76 + panda/board/stm32h7/inc/cmsis_compiler.h | 284 + panda/board/stm32h7/inc/cmsis_gcc.h | 2169 ++ panda/board/stm32h7/inc/cmsis_version.h | 40 + panda/board/stm32h7/inc/core_cm7.h | 2725 ++ panda/board/stm32h7/inc/mpu_armv8.h | 346 + panda/board/stm32h7/inc/stm32h725xx.h | 24740 ++++++++++++++++ panda/board/stm32h7/inc/stm32h735xx.h | 24740 ++++++++++++++++ panda/board/stm32h7/inc/stm32h7xx.h | 243 + panda/board/stm32h7/inc/stm32h7xx_hal_def.h | 221 + .../board/stm32h7/inc/stm32h7xx_hal_gpio_ex.h | 489 + panda/board/stm32h7/inc/system_stm32h7xx.h | 105 + panda/board/stm32h7/interrupt_handlers.h | 76 + panda/board/stm32h7/lladc.h | 39 + panda/board/stm32h7/lldac.h | 42 + panda/board/stm32h7/llexti.h | 63 + panda/board/stm32h7/llfan.h | 23 + panda/board/stm32h7/llfdcan.h | 269 + panda/board/stm32h7/llflash.h | 33 + panda/board/stm32h7/lli2c.h | 153 + panda/board/stm32h7/llspi.h | 106 + panda/board/stm32h7/lluart.h | 106 + panda/board/stm32h7/llusb.h | 91 + panda/board/stm32h7/peripherals.h | 138 + panda/board/stm32h7/stm32h7_config.h | 99 + panda/board/utils.h | 43 + panda/crypto/hash-internal.h | 63 + panda/crypto/rsa.h | 58 + panda/crypto/sha.h | 51 + panda/docs/CANPacket_structure.png | Bin 0 -> 39535 bytes panda/docs/USB_packet_structure.png | Bin 0 -> 21782 bytes panda/drivers/linux/.gitignore | 6 + panda/drivers/linux/Makefile | 24 + panda/drivers/linux/README.md | 28 + panda/drivers/linux/dkms.conf | 6 + panda/drivers/linux/panda.c | 609 + panda/drivers/linux/test/Makefile | 2 + panda/drivers/linux/test/main.c | 120 + panda/drivers/linux/test/run.sh | 4 + panda/drivers/spi/.gitignore | 7 + panda/drivers/spi/Makefile | 14 + panda/drivers/spi/load.sh | 27 + panda/drivers/spi/patch | 33 + panda/drivers/spi/pull-src.sh | 12 + panda/drivers/spi/spi_panda.h | 160 + panda/drivers/spi/spidev_panda.c | 891 + panda/examples/__init__.py | 0 panda/examples/can_bit_transition.md | 25 + panda/examples/can_bit_transition.py | 111 + panda/examples/can_logger.py | 43 + panda/examples/can_unique.md | 103 + panda/examples/can_unique.py | 116 + panda/examples/query_fw_versions.py | 3 +- panda/examples/query_vin_and_stats.py | 57 + panda/examples/tesla_tester.py | 50 + panda/mypy.ini | 11 + panda/panda.png | Bin 0 -> 72509 bytes panda/pyproject.toml | 14 + panda/python/__init__.py | 28 +- panda/python/constants.py | 4 +- panda/python/dfu.py | 25 +- panda/python/isotp.py | 4 +- panda/python/serial.py | 2 +- panda/python/spi.py | 6 +- panda/python/uds.py | 89 +- panda/python/xcp.py | 258 + panda/release/.gitignore | 1 + panda/release/make_release.sh | 22 + panda/requirements.txt | 5 +- panda/setup.py | 2 - panda/tests/__init__.py | 0 panda/tests/benchmark.py | 43 + panda/tests/black_white_loopback_test.py | 156 + panda/tests/black_white_relay_endurance.py | 164 + panda/tests/black_white_relay_test.py | 135 + panda/tests/bulk_write_test.py | 48 + panda/tests/can_printer.py | 36 + panda/tests/canfd/test_canfd.py | 152 + panda/tests/check_fw_size.py | 97 + panda/tests/ci_shell.sh | 20 + panda/tests/debug_console.py | 62 + .../development/register_hashmap_spread.py | 50 + panda/tests/echo.py | 16 + panda/tests/elm_car_simulator.py | 232 + panda/tests/elm_throughput.py | 44 + panda/tests/fan/fan_test.py | 14 + panda/tests/fan/fan_tuning.py | 88 + panda/tests/get_version.py | 7 + panda/tests/gmbitbang/recv.py | 18 + panda/tests/gmbitbang/rigol.py | 36 + panda/tests/gmbitbang/test.py | 32 + panda/tests/gmbitbang/test_one.py | 22 + panda/tests/gmbitbang/test_packer.c | 28 + panda/tests/gmlan_harness_test.py | 78 + panda/tests/health_test.py | 18 + panda/tests/hitl/1_program.py | 103 + panda/tests/hitl/2_health.py | 125 + panda/tests/hitl/3_usb_to_can.py | 127 + panda/tests/hitl/4_can_loopback.py | 202 + panda/tests/hitl/5_spi.py | 103 + panda/tests/hitl/6_safety.py | 29 + panda/tests/hitl/7_internal.py | 68 + panda/tests/hitl/__init__.py | 0 panda/tests/hitl/conftest.py | 225 + panda/tests/hitl/helpers.py | 71 + .../hitl/known_bootstub/bootstub.panda_h7.bin | Bin 0 -> 16824 bytes ...bootstub_f4_first_dos_production.panda.bin | Bin 0 -> 14220 bytes .../bootstub_f4_only_bcd.panda.bin | Bin 0 -> 14208 bytes panda/tests/hitl/reset_jungles.py | 42 + panda/tests/hitl/run_parallel_tests.sh | 8 + panda/tests/hitl/run_serial_tests.sh | 7 + panda/tests/ir_test.py | 14 + panda/tests/libpanda/SConscript | 42 + panda/tests/libpanda/libpanda_py.py | 98 + panda/tests/libpanda/panda.c | 33 + panda/tests/libpanda/safety_helpers.h | 195 + panda/tests/libpanda/safety_helpers.py | 106 + panda/tests/libs/resetter.py | 57 + panda/tests/loopback_test.py | 94 + panda/tests/message_drop_test.py | 70 + panda/tests/misra/.gitignore | 5 + panda/tests/misra/coverage_table | 156 + panda/tests/misra/install.sh | 19 + panda/tests/misra/test_misra.sh | 61 + panda/tests/misra/test_mutation.py | 84 + panda/tests/read_flash_spi.py | 32 + panda/tests/read_st_flash.sh | 6 + panda/tests/read_winusb_descriptors.py | 34 + panda/tests/reflash_internal_panda.py | 49 + panda/tests/relay_test.py | 16 + panda/tests/restore_flash_spi.py | 27 + panda/tests/safety/__init__.py | 0 panda/tests/safety/common.py | 1071 + panda/tests/safety/hyundai_common.py | 157 + panda/tests/safety/test.sh | 34 + panda/tests/safety/test_body.py | 70 + panda/tests/safety/test_chrysler.py | 125 + panda/tests/safety/test_defaults.py | 73 + panda/tests/safety/test_elm327.py | 48 + panda/tests/safety/test_ford.py | 476 + panda/tests/safety/test_gm.py | 383 + panda/tests/safety/test_honda.py | 618 + panda/tests/safety/test_hyundai.py | 216 + panda/tests/safety/test_hyundai_canfd.py | 272 + panda/tests/safety/test_mazda.py | 86 + panda/tests/safety/test_nissan.py | 117 + panda/tests/safety/test_subaru.py | 228 + panda/tests/safety/test_subaru_preglobal.py | 70 + panda/tests/safety/test_tesla.py | 185 + panda/tests/safety/test_toyota.py | 367 + panda/tests/safety/test_volkswagen_mqb.py | 225 + panda/tests/safety/test_volkswagen_pq.py | 199 + panda/tests/safety_replay/.gitignore | 1 + panda/tests/safety_replay/__init__.py | 0 panda/tests/safety_replay/helpers.py | 80 + panda/tests/safety_replay/replay_drive.py | 103 + panda/tests/setup_device_ci.sh | 74 + panda/tests/som/on-device.py | 24 + panda/tests/som/test_bootkick.py | 154 + panda/tests/som_debug.sh | 7 + panda/tests/spam_can.py | 20 + panda/tests/standalone_test.py | 32 + panda/tests/test_rsa.c | 34 + panda/tests/tucan_loopback.py | 92 + panda/tests/usbprotocol/test.sh | 8 + panda/tests/usbprotocol/test_comms.py | 160 + panda/tests/usbprotocol/test_pandalib.py | 26 + 306 files changed, 108529 insertions(+), 119 deletions(-) create mode 100644 panda/.github/workflows/drivers.yaml create mode 100644 panda/.github/workflows/repo.yml create mode 100644 panda/.github/workflows/test.yaml create mode 100644 panda/.pre-commit-config.yaml create mode 100644 panda/Dockerfile create mode 100644 panda/Jenkinsfile create mode 100644 panda/LICENSE create mode 100644 panda/README.md create mode 100644 panda/SConscript create mode 100644 panda/SConstruct create mode 100644 panda/board/SConscript create mode 100644 panda/board/boards/black.h create mode 100644 panda/board/boards/board_declarations.h create mode 100644 panda/board/boards/cuatro.h create mode 100644 panda/board/boards/dos.h create mode 100644 panda/board/boards/grey.h create mode 100644 panda/board/boards/red.h create mode 100644 panda/board/boards/red_chiplet.h create mode 100644 panda/board/boards/tres.h create mode 100644 panda/board/boards/uno.h create mode 100644 panda/board/boards/unused_funcs.h create mode 100644 panda/board/boards/white.h create mode 100644 panda/board/bootstub_declarations.h create mode 100644 panda/board/can_comms.h create mode 100644 panda/board/can_definitions.h create mode 100644 panda/board/comms_definitions.h create mode 100644 panda/board/config.h create mode 100644 panda/board/crc.h create mode 100644 panda/board/critical.h create mode 100644 panda/board/drivers/bootkick.h create mode 100644 panda/board/drivers/bxcan.h create mode 100644 panda/board/drivers/can_common.h create mode 100644 panda/board/drivers/clock_source.h create mode 100644 panda/board/drivers/fake_siren.h create mode 100644 panda/board/drivers/fan.h create mode 100644 panda/board/drivers/fdcan.h create mode 100644 panda/board/drivers/gmlan_alt.h create mode 100644 panda/board/drivers/gpio.h create mode 100644 panda/board/drivers/harness.h create mode 100644 panda/board/drivers/interrupts.h create mode 100644 panda/board/drivers/pwm.h create mode 100644 panda/board/drivers/registers.h create mode 100644 panda/board/drivers/simple_watchdog.h create mode 100644 panda/board/drivers/spi.h create mode 100644 panda/board/drivers/timers.h create mode 100644 panda/board/drivers/uart.h create mode 100644 panda/board/drivers/usb.h create mode 100644 panda/board/drivers/watchdog.h create mode 100644 panda/board/early_init.h create mode 100644 panda/board/fake_stm.h create mode 100644 panda/board/faults.h create mode 100644 panda/board/flasher.h create mode 100644 panda/board/health.h create mode 100644 panda/board/jungle/SConscript create mode 100644 panda/board/jungle/boards/board_declarations.h create mode 100644 panda/board/jungle/boards/board_v1.h create mode 100644 panda/board/jungle/boards/board_v2.h create mode 100644 panda/board/jungle/jungle_health.h create mode 100644 panda/board/jungle/main_comms.h delete mode 100755 panda/board/jungle/obj/bootstub.panda_jungle.bin delete mode 100755 panda/board/jungle/obj/bootstub.panda_jungle.elf delete mode 100755 panda/board/jungle/obj/bootstub.panda_jungle_h7.bin delete mode 100755 panda/board/jungle/obj/bootstub.panda_jungle_h7.elf delete mode 100755 panda/board/jungle/obj/panda_jungle.bin delete mode 100644 panda/board/jungle/obj/panda_jungle.bin.signed delete mode 100755 panda/board/jungle/obj/panda_jungle.elf delete mode 100755 panda/board/jungle/obj/panda_jungle_h7.bin delete mode 100644 panda/board/jungle/obj/panda_jungle_h7.bin.signed delete mode 100755 panda/board/jungle/obj/panda_jungle_h7.elf create mode 100644 panda/board/jungle/stm32f4/board.h create mode 100644 panda/board/jungle/stm32h7/board.h create mode 100644 panda/board/jungle/stm32h7/lladc.h create mode 100644 panda/board/libc.h create mode 100644 panda/board/main_comms.h create mode 100644 panda/board/main_declarations.h delete mode 100755 panda/board/obj/bootstub.panda.bin delete mode 100755 panda/board/obj/bootstub.panda.elf delete mode 100755 panda/board/obj/bootstub.panda_h7.bin delete mode 100755 panda/board/obj/bootstub.panda_h7.elf delete mode 100755 panda/board/obj/panda.bin delete mode 100644 panda/board/obj/panda.bin.signed delete mode 100755 panda/board/obj/panda.elf delete mode 100755 panda/board/obj/panda_h7.bin delete mode 100644 panda/board/obj/panda_h7.bin.signed delete mode 100755 panda/board/obj/panda_h7.elf delete mode 100644 panda/board/obj/version create mode 100644 panda/board/power_saving.h create mode 100644 panda/board/provision.h create mode 100644 panda/board/safety.h create mode 100644 panda/board/safety/safety_body.h create mode 100644 panda/board/safety/safety_chrysler.h create mode 100644 panda/board/safety/safety_defaults.h create mode 100644 panda/board/safety/safety_elm327.h create mode 100644 panda/board/safety/safety_ford.h create mode 100644 panda/board/safety/safety_gm.h create mode 100644 panda/board/safety/safety_honda.h create mode 100644 panda/board/safety/safety_hyundai.h create mode 100644 panda/board/safety/safety_hyundai_canfd.h create mode 100644 panda/board/safety/safety_hyundai_common.h create mode 100644 panda/board/safety/safety_mazda.h create mode 100644 panda/board/safety/safety_nissan.h create mode 100644 panda/board/safety/safety_subaru.h create mode 100644 panda/board/safety/safety_subaru_preglobal.h create mode 100644 panda/board/safety/safety_tesla.h create mode 100644 panda/board/safety/safety_toyota.h create mode 100644 panda/board/safety/safety_volkswagen_common.h create mode 100644 panda/board/safety/safety_volkswagen_mqb.h create mode 100644 panda/board/safety/safety_volkswagen_pq.h create mode 100644 panda/board/safety_declarations.h create mode 100644 panda/board/stm32f4/board.h create mode 100644 panda/board/stm32f4/clock.h create mode 100644 panda/board/stm32f4/inc/cmsis_compiler.h create mode 100644 panda/board/stm32f4/inc/cmsis_gcc.h create mode 100644 panda/board/stm32f4/inc/cmsis_version.h create mode 100644 panda/board/stm32f4/inc/core_cm3.h create mode 100644 panda/board/stm32f4/inc/core_cm4.h create mode 100644 panda/board/stm32f4/inc/mpu_armv7.h create mode 100644 panda/board/stm32f4/inc/stm32f413xx.h create mode 100644 panda/board/stm32f4/inc/stm32f4xx.h create mode 100644 panda/board/stm32f4/inc/stm32f4xx_hal_def.h create mode 100644 panda/board/stm32f4/inc/stm32f4xx_hal_gpio_ex.h create mode 100644 panda/board/stm32f4/inc/system_stm32f4xx.h create mode 100644 panda/board/stm32f4/interrupt_handlers.h create mode 100644 panda/board/stm32f4/lladc.h create mode 100644 panda/board/stm32f4/llbxcan.h create mode 100644 panda/board/stm32f4/llexti.h create mode 100644 panda/board/stm32f4/llfan.h create mode 100644 panda/board/stm32f4/llflash.h create mode 100644 panda/board/stm32f4/llspi.h create mode 100644 panda/board/stm32f4/lluart.h create mode 100644 panda/board/stm32f4/llusb.h create mode 100644 panda/board/stm32f4/peripherals.h rename panda/board/{stm32fx => stm32f4}/startup_stm32f413xx.s (100%) create mode 100644 panda/board/stm32f4/stm32f4_config.h rename panda/board/{stm32fx => stm32f4}/stm32f4_flash.ld (100%) create mode 100644 panda/board/stm32h7/board.h create mode 100644 panda/board/stm32h7/clock.h create mode 100644 panda/board/stm32h7/inc/cmsis_compiler.h create mode 100644 panda/board/stm32h7/inc/cmsis_gcc.h create mode 100644 panda/board/stm32h7/inc/cmsis_version.h create mode 100644 panda/board/stm32h7/inc/core_cm7.h create mode 100644 panda/board/stm32h7/inc/mpu_armv8.h create mode 100644 panda/board/stm32h7/inc/stm32h725xx.h create mode 100644 panda/board/stm32h7/inc/stm32h735xx.h create mode 100644 panda/board/stm32h7/inc/stm32h7xx.h create mode 100644 panda/board/stm32h7/inc/stm32h7xx_hal_def.h create mode 100644 panda/board/stm32h7/inc/stm32h7xx_hal_gpio_ex.h create mode 100644 panda/board/stm32h7/inc/system_stm32h7xx.h create mode 100644 panda/board/stm32h7/interrupt_handlers.h create mode 100644 panda/board/stm32h7/lladc.h create mode 100644 panda/board/stm32h7/lldac.h create mode 100644 panda/board/stm32h7/llexti.h create mode 100644 panda/board/stm32h7/llfan.h create mode 100644 panda/board/stm32h7/llfdcan.h create mode 100644 panda/board/stm32h7/llflash.h create mode 100644 panda/board/stm32h7/lli2c.h create mode 100644 panda/board/stm32h7/llspi.h create mode 100644 panda/board/stm32h7/lluart.h create mode 100644 panda/board/stm32h7/llusb.h create mode 100644 panda/board/stm32h7/peripherals.h create mode 100644 panda/board/stm32h7/stm32h7_config.h create mode 100644 panda/board/utils.h create mode 100644 panda/crypto/hash-internal.h create mode 100644 panda/crypto/rsa.h create mode 100644 panda/crypto/sha.h create mode 100644 panda/docs/CANPacket_structure.png create mode 100644 panda/docs/USB_packet_structure.png create mode 100644 panda/drivers/linux/.gitignore create mode 100644 panda/drivers/linux/Makefile create mode 100644 panda/drivers/linux/README.md create mode 100644 panda/drivers/linux/dkms.conf create mode 100644 panda/drivers/linux/panda.c create mode 100644 panda/drivers/linux/test/Makefile create mode 100644 panda/drivers/linux/test/main.c create mode 100644 panda/drivers/linux/test/run.sh create mode 100644 panda/drivers/spi/.gitignore create mode 100644 panda/drivers/spi/Makefile create mode 100644 panda/drivers/spi/load.sh create mode 100644 panda/drivers/spi/patch create mode 100644 panda/drivers/spi/pull-src.sh create mode 100644 panda/drivers/spi/spi_panda.h create mode 100644 panda/drivers/spi/spidev_panda.c create mode 100644 panda/examples/__init__.py create mode 100644 panda/examples/can_bit_transition.md create mode 100644 panda/examples/can_bit_transition.py create mode 100644 panda/examples/can_logger.py create mode 100644 panda/examples/can_unique.md create mode 100644 panda/examples/can_unique.py create mode 100644 panda/examples/query_vin_and_stats.py create mode 100644 panda/examples/tesla_tester.py create mode 100644 panda/mypy.ini create mode 100644 panda/panda.png create mode 100644 panda/pyproject.toml create mode 100644 panda/python/xcp.py create mode 100644 panda/release/.gitignore create mode 100644 panda/release/make_release.sh create mode 100644 panda/tests/__init__.py create mode 100644 panda/tests/benchmark.py create mode 100644 panda/tests/black_white_loopback_test.py create mode 100644 panda/tests/black_white_relay_endurance.py create mode 100644 panda/tests/black_white_relay_test.py create mode 100644 panda/tests/bulk_write_test.py create mode 100644 panda/tests/can_printer.py create mode 100644 panda/tests/canfd/test_canfd.py create mode 100644 panda/tests/check_fw_size.py create mode 100644 panda/tests/ci_shell.sh create mode 100644 panda/tests/debug_console.py create mode 100644 panda/tests/development/register_hashmap_spread.py create mode 100644 panda/tests/echo.py create mode 100644 panda/tests/elm_car_simulator.py create mode 100644 panda/tests/elm_throughput.py create mode 100644 panda/tests/fan/fan_test.py create mode 100644 panda/tests/fan/fan_tuning.py create mode 100644 panda/tests/get_version.py create mode 100644 panda/tests/gmbitbang/recv.py create mode 100644 panda/tests/gmbitbang/rigol.py create mode 100644 panda/tests/gmbitbang/test.py create mode 100644 panda/tests/gmbitbang/test_one.py create mode 100644 panda/tests/gmbitbang/test_packer.c create mode 100644 panda/tests/gmlan_harness_test.py create mode 100644 panda/tests/health_test.py create mode 100644 panda/tests/hitl/1_program.py create mode 100644 panda/tests/hitl/2_health.py create mode 100644 panda/tests/hitl/3_usb_to_can.py create mode 100644 panda/tests/hitl/4_can_loopback.py create mode 100644 panda/tests/hitl/5_spi.py create mode 100644 panda/tests/hitl/6_safety.py create mode 100644 panda/tests/hitl/7_internal.py create mode 100644 panda/tests/hitl/__init__.py create mode 100644 panda/tests/hitl/conftest.py create mode 100644 panda/tests/hitl/helpers.py create mode 100644 panda/tests/hitl/known_bootstub/bootstub.panda_h7.bin create mode 100644 panda/tests/hitl/known_bootstub/bootstub_f4_first_dos_production.panda.bin create mode 100644 panda/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin create mode 100644 panda/tests/hitl/reset_jungles.py create mode 100644 panda/tests/hitl/run_parallel_tests.sh create mode 100644 panda/tests/hitl/run_serial_tests.sh create mode 100644 panda/tests/ir_test.py create mode 100644 panda/tests/libpanda/SConscript create mode 100644 panda/tests/libpanda/libpanda_py.py create mode 100644 panda/tests/libpanda/panda.c create mode 100644 panda/tests/libpanda/safety_helpers.h create mode 100644 panda/tests/libpanda/safety_helpers.py create mode 100644 panda/tests/libs/resetter.py create mode 100644 panda/tests/loopback_test.py create mode 100644 panda/tests/message_drop_test.py create mode 100644 panda/tests/misra/.gitignore create mode 100644 panda/tests/misra/coverage_table create mode 100644 panda/tests/misra/install.sh create mode 100644 panda/tests/misra/test_misra.sh create mode 100644 panda/tests/misra/test_mutation.py create mode 100644 panda/tests/read_flash_spi.py create mode 100644 panda/tests/read_st_flash.sh create mode 100644 panda/tests/read_winusb_descriptors.py create mode 100644 panda/tests/reflash_internal_panda.py create mode 100644 panda/tests/relay_test.py create mode 100644 panda/tests/restore_flash_spi.py create mode 100644 panda/tests/safety/__init__.py create mode 100644 panda/tests/safety/common.py create mode 100644 panda/tests/safety/hyundai_common.py create mode 100644 panda/tests/safety/test.sh create mode 100644 panda/tests/safety/test_body.py create mode 100644 panda/tests/safety/test_chrysler.py create mode 100644 panda/tests/safety/test_defaults.py create mode 100644 panda/tests/safety/test_elm327.py create mode 100644 panda/tests/safety/test_ford.py create mode 100644 panda/tests/safety/test_gm.py create mode 100644 panda/tests/safety/test_honda.py create mode 100644 panda/tests/safety/test_hyundai.py create mode 100644 panda/tests/safety/test_hyundai_canfd.py create mode 100644 panda/tests/safety/test_mazda.py create mode 100644 panda/tests/safety/test_nissan.py create mode 100644 panda/tests/safety/test_subaru.py create mode 100644 panda/tests/safety/test_subaru_preglobal.py create mode 100644 panda/tests/safety/test_tesla.py create mode 100644 panda/tests/safety/test_toyota.py create mode 100644 panda/tests/safety/test_volkswagen_mqb.py create mode 100644 panda/tests/safety/test_volkswagen_pq.py create mode 100644 panda/tests/safety_replay/.gitignore create mode 100644 panda/tests/safety_replay/__init__.py create mode 100644 panda/tests/safety_replay/helpers.py create mode 100644 panda/tests/safety_replay/replay_drive.py create mode 100644 panda/tests/setup_device_ci.sh create mode 100644 panda/tests/som/on-device.py create mode 100644 panda/tests/som/test_bootkick.py create mode 100644 panda/tests/som_debug.sh create mode 100644 panda/tests/spam_can.py create mode 100644 panda/tests/standalone_test.py create mode 100644 panda/tests/test_rsa.c create mode 100644 panda/tests/tucan_loopback.py create mode 100644 panda/tests/usbprotocol/test.sh create mode 100644 panda/tests/usbprotocol/test_comms.py create mode 100644 panda/tests/usbprotocol/test_pandalib.py diff --git a/panda/.github/workflows/drivers.yaml b/panda/.github/workflows/drivers.yaml new file mode 100644 index 0000000..c425d4c --- /dev/null +++ b/panda/.github/workflows/drivers.yaml @@ -0,0 +1,23 @@ +name: drivers +on: [push, pull_request] + +jobs: + build_socketcan: + name: socketcan build + runs-on: ubuntu-20.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get install -y dkms gcc linux-headers-$(uname -r) make + - name: Build socketcan driver + run: | + cd drivers/linux + make link + make build + make install + - name: Print make log + if: always() + continue-on-error: true + run: cat /var/lib/dkms/panda/*/build/make.log diff --git a/panda/.github/workflows/repo.yml b/panda/.github/workflows/repo.yml new file mode 100644 index 0000000..1e3dcf9 --- /dev/null +++ b/panda/.github/workflows/repo.yml @@ -0,0 +1,28 @@ +name: repo + +on: + schedule: + - cron: "0 15 * * 2" + workflow_dispatch: + +jobs: + pre-commit-autoupdate: + name: pre-commit autoupdate + runs-on: ubuntu-20.04 + container: + image: ghcr.io/commaai/panda:latest + steps: + - uses: actions/checkout@v3 + - name: pre-commit autoupdate + run: | + git config --global --add safe.directory '*' + pre-commit autoupdate + - name: Create Pull Request + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + with: + token: ${{ secrets.ACTIONS_CREATE_PR_PAT }} + commit-message: Update pre-commit hook versions + title: 'pre-commit: autoupdate hooks' + branch: pre-commit-updates + base: master + delete-branch: true diff --git a/panda/.github/workflows/test.yaml b/panda/.github/workflows/test.yaml new file mode 100644 index 0000000..968ee3e --- /dev/null +++ b/panda/.github/workflows/test.yaml @@ -0,0 +1,119 @@ +name: tests + +on: + push: + branches: + - master + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref != 'refs/heads/master' && github.ref || github.run_id }}-${{ github.event_name }} + cancel-in-progress: true + +env: + RUN: docker run -v ${{ github.workspace }}:/tmp/openpilot/panda -w /tmp/openpilot/panda --rm panda /bin/bash -c + BUILD: | + export DOCKER_BUILDKIT=1 + docker build --pull --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from ghcr.io/commaai/panda:latest -t panda -f Dockerfile . + + PYTHONWARNINGS: "error" + +jobs: + docker_push: + name: docker push + runs-on: ubuntu-20.04 + timeout-minutes: 20 + if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/panda' + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + timeout-minutes: 11 + run: eval "$BUILD" + - name: Login to dockerhub + run: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} + - name: Tag image + run: docker tag panda ghcr.io/commaai/panda:latest + - name: Push image + run: docker push ghcr.io/commaai/panda:latest + + build: + name: build + runs-on: ubuntu-20.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Test python package installer + run: ${{ env.RUN }} "python setup.py install" + - name: Build panda images and bootstub + run: ${{ env.RUN }} "scons -j4" + - name: Build panda with SPI support + run: ${{ env.RUN }} "ENABLE_SPI=1 scons -j4" + - name: Build with UBSan + run: ${{ env.RUN }} "scons -j4 --ubsan" + - name: Build jungle firmware with FINAL_PROVISIONING support + run: ${{ env.RUN }} "FINAL_PROVISIONING=1 scons -j4 board/jungle" + + unit_tests: + name: unit tests + runs-on: ubuntu-20.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Build panda + run: $RUN "scons -j4" + - name: Test communication protocols + run: $RUN "cd tests/usbprotocol && ./test.sh" + + safety: + name: safety + runs-on: ubuntu-20.04 + strategy: + matrix: + flags: ['', '--ubsan'] + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Run safety tests + timeout-minutes: 5 + run: | + ${{ env.RUN }} "cd .. && \ + scons -c && \ + scons -j$(nproc) opendbc/ cereal/ && \ + cd panda && \ + scons -j$(nproc) ${{ matrix.flags }} && \ + tests/safety/test.sh" + + misra: + name: MISRA C:2012 + runs-on: ubuntu-20.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Build FW + run: ${{ env.RUN }} "scons -j$(nproc)" + - name: Run MISRA C:2012 analysis + timeout-minutes: 1 + run: ${{ env.RUN }} "cd tests/misra && ./test_misra.sh" + - name: MISRA mutation tests + timeout-minutes: 4 + run: ${{ env.RUN }} "cd tests/misra && ./test_mutation.py" + + python_linter: + name: python linter + runs-on: ubuntu-20.04 + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Run static analysis + timeout-minutes: 3 + run: ${{ env.RUN }} "pre-commit run --all" diff --git a/panda/.pre-commit-config.yaml b/panda/.pre-commit-config.yaml new file mode 100644 index 0000000..402bdc3 --- /dev/null +++ b/panda/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-ast + - id: check-yaml + - id: check-merge-conflict + - id: check-symlinks + - id: check-executables-have-shebangs + - id: check-shebang-scripts-are-executable +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.8.0 + hooks: + - id: mypy + additional_dependencies: ['git+https://github.com/numpy/numpy-stubs', 'types-requests', 'types-atomicwrites', + 'types-pycurl'] +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.0 + hooks: + - id: ruff diff --git a/panda/Dockerfile b/panda/Dockerfile new file mode 100644 index 0000000..99bedcf --- /dev/null +++ b/panda/Dockerfile @@ -0,0 +1,91 @@ +FROM ubuntu:20.04 +ENV PYTHONUNBUFFERED 1 +ENV PYTHONPATH /tmp/openpilot:$PYTHONPATH + +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + autoconf \ + automake \ + bzip2 \ + ca-certificates \ + capnproto \ + clang \ + curl \ + g++ \ + gcc-arm-none-eabi libnewlib-arm-none-eabi \ + git \ + libarchive-dev \ + libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libavresample-dev libavfilter-dev \ + libbz2-dev \ + libcapnp-dev \ + libcurl4-openssl-dev \ + libffi-dev \ + libtool \ + libssl-dev \ + libsqlite3-dev \ + libusb-1.0-0 \ + libzmq3-dev \ + locales \ + opencl-headers \ + ocl-icd-opencl-dev \ + make \ + patch \ + pkg-config \ + python \ + python-dev \ + qt5-default \ + unzip \ + wget \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* && \ + cd /usr/lib/gcc/arm-none-eabi/9.2.1 && \ + rm -rf arm/ && \ + rm -rf thumb/nofp thumb/v6* thumb/v8* thumb/v7+fp thumb/v7-r+fp.sp + +RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + +RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash +ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}" + +ENV PANDA_PATH=/tmp/openpilot/panda +ENV OPENPILOT_REF="5690386d8d731c9bebda536a5c71c890f6dfe98c" +ENV OPENDBC_REF="1745ab51825055cd18748013c4a5e3377319e390" + +COPY requirements.txt /tmp/ +RUN pyenv install 3.11.4 && \ + pyenv global 3.11.4 && \ + pyenv rehash && \ + pip install --no-cache-dir -r /tmp/requirements.txt + +ENV CPPCHECK_DIR=/tmp/cppcheck +COPY tests/misra/install.sh /tmp/ +RUN /tmp/install.sh + +RUN git config --global --add safe.directory /tmp/openpilot/panda +RUN cd /tmp && \ + git clone https://github.com/commaai/openpilot.git tmppilot || true && \ + cd /tmp/tmppilot && \ + git fetch origin $OPENPILOT_REF && \ + git checkout $OPENPILOT_REF && \ + git submodule update --init cereal opendbc rednose_repo body && \ + git -C opendbc fetch && \ + git -C opendbc checkout $OPENDBC_REF && \ + git -C opendbc reset --hard HEAD && \ + git -C opendbc clean -xfd && \ + mkdir /tmp/openpilot && \ + cp -pR SConstruct site_scons/ tools/ selfdrive/ system/ common/ cereal/ opendbc/ rednose/ third_party/ body/ /tmp/openpilot && \ + rm -rf /tmp/openpilot/panda && \ + rm -rf /tmp/tmppilot + +RUN cd /tmp/openpilot && \ + pip install --no-cache-dir -r opendbc/requirements.txt && \ + pip install --no-cache-dir --upgrade aenum lru-dict pycurl tenacity atomicwrites serial smbus2 + + +# for Jenkins +COPY README.md panda.tar.* /tmp/ +RUN mkdir /tmp/openpilot/panda && \ + tar -xvf /tmp/panda.tar.gz -C /tmp/openpilot/panda/ || true diff --git a/panda/Jenkinsfile b/panda/Jenkinsfile new file mode 100644 index 0000000..25b8490 --- /dev/null +++ b/panda/Jenkinsfile @@ -0,0 +1,156 @@ +def docker_run(String step_label, int timeout_mins, String cmd) { + timeout(time: timeout_mins, unit: 'MINUTES') { + sh script: "docker run --rm --privileged \ + --env PYTHONWARNINGS=error \ + --volume /dev/bus/usb:/dev/bus/usb \ + --volume /var/run/dbus:/var/run/dbus \ + --workdir /tmp/openpilot/panda \ + --net host \ + ${env.DOCKER_IMAGE_TAG} \ + bash -c 'scons -j8 && ${cmd}'", \ + label: step_label + } +} + + +def phone(String ip, String step_label, String cmd) { + withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { + def ssh_cmd = """ +ssh -tt -o StrictHostKeyChecking=no -i ${key_file} 'comma@${ip}' /usr/bin/bash <<'END' + +set -e + + +source ~/.bash_profile +if [ -f /etc/profile ]; then + source /etc/profile +fi + +export CI=1 +export TEST_DIR=${env.TEST_DIR} +export SOURCE_DIR=${env.SOURCE_DIR} +export GIT_BRANCH=${env.GIT_BRANCH} +export GIT_COMMIT=${env.GIT_COMMIT} +export PYTHONPATH=${env.TEST_DIR}/../ +export PYTHONWARNINGS=error + +cd ${env.TEST_DIR} || true +${cmd} +exit 0 + +END""" + + sh script: ssh_cmd, label: step_label + } +} + +def phone_steps(String device_type, steps) { + lock(resource: "", label: device_type, inversePrecedence: true, variable: 'device_ip', quantity: 1) { + timeout(time: 20, unit: 'MINUTES') { + phone(device_ip, "git checkout", readFile("tests/setup_device_ci.sh"),) + steps.each { item -> + phone(device_ip, item[0], item[1]) + } + } + } +} + + + +pipeline { + agent any + environment { + CI = "1" + PYTHONWARNINGS= "error" + DOCKER_IMAGE_TAG = "panda:build-${env.GIT_COMMIT}" + + TEST_DIR = "/data/panda" + SOURCE_DIR = "/data/panda_source/" + } + options { + timeout(time: 3, unit: 'HOURS') + disableConcurrentBuilds(abortPrevious: env.BRANCH_NAME != 'master') + } + + stages { + stage('panda tests') { + parallel { + stage('test dos') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("panda-dos", [ + ["build", "scons -j4"], + ["flash", "cd tests/ && ./reflash_internal_panda.py"], + ["flash jungle", "cd board/jungle && ./flash.py"], + ["test", "cd tests/hitl && HW_TYPES=6 pytest -n0 --durations=0 [2-7]*.py -k 'not test_send_recv'"], + ]) + } + } + + stage('test tres') { + agent { docker { image 'ghcr.io/commaai/alpine-ssh'; args '--user=root' } } + steps { + phone_steps("panda-tres", [ + ["build", "scons -j4"], + ["flash", "cd tests/ && ./reflash_internal_panda.py"], + ["flash jungle", "cd board/jungle && ./flash.py"], + ["test", "cd tests/hitl && HW_TYPES=9 pytest -n0 --durations=0 2*.py [5-9]*.py"], + ]) + } + } + + stage ('Acquire resource locks') { + options { + lock(resource: "pandas") + } + stages { + stage('Build Docker Image') { + steps { + timeout(time: 20, unit: 'MINUTES') { + script { + sh 'git archive -v -o panda.tar.gz --format=tar.gz HEAD' + dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}") + } + } + } + } + stage('jungle tests') { + steps { + script { + retry (3) { + docker_run("reset hardware", 3, "python ./tests/hitl/reset_jungles.py") + } + } + } + } + stage('bootkick tests') { + steps { + script { + docker_run("test", 10, "pytest -n0 ./tests/som/test_bootkick.py") + } + } + } + + /* + stage('HITL tests') { + steps { + script { + docker_run("parallel tests", 5, 'PANDAS_JUNGLE=23002d000851393038373731 PANDAS_EXCLUDE="1d0002000c51303136383232 2f002e000c51303136383232" ./tests/hitl/run_parallel_tests.sh') + docker_run("serial tests", 9, 'PANDAS_JUNGLE=23002d000851393038373731 PANDAS_EXCLUDE="1d0002000c51303136383232 2f002e000c51303136383232" ./tests/hitl/run_serial_tests.sh') + } + } + } + stage('CANFD tests') { + steps { + script { + docker_run("CANFD tets", 6, 'JUNGLE=058010800f51363038363036 H7_PANDAS_EXCLUDE="080021000c51303136383232 33000e001051393133353939" ./tests/canfd/test_canfd.py') + } + } + } + */ + } + } + } + } + } +} diff --git a/panda/LICENSE b/panda/LICENSE new file mode 100644 index 0000000..8a6c697 --- /dev/null +++ b/panda/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2016, Comma.ai, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/panda/README.md b/panda/README.md new file mode 100644 index 0000000..10e66cf --- /dev/null +++ b/panda/README.md @@ -0,0 +1,104 @@ +# Welcome to panda + +![panda tests](https://github.com/commaai/panda/workflows/tests/badge.svg) +![panda drivers](https://github.com/commaai/panda/workflows/drivers/badge.svg) + +panda speaks CAN and CAN FD, and it runs on [STM32F413](https://www.st.com/resource/en/reference_manual/rm0430-stm32f413423-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) and [STM32H725](https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf). + +## Directory structure + +``` +. +├── board # Code that runs on the STM32 +├── drivers # Drivers (not needed for use with Python) +├── python # Python userspace library for interfacing with the panda +├── tests # Tests and helper programs for panda +``` + +## Safety Model + +When a panda powers up, by default it's in `SAFETY_SILENT` mode. While in `SAFETY_SILENT` mode, the CAN buses are forced to be silent. In order to send messages, you have to select a safety mode. Some of safety modes (for example `SAFETY_ALLOUTPUT`) are disabled in release firmwares. In order to use them, compile and flash your own build. + +Safety modes optionally support `controls_allowed`, which allows or blocks a subset of messages based on a customizable state in the board. + +## Code Rigor + +The panda firmware is written for its use in conjuction with [openpilot](https://github.com/commaai/openpilot). The panda firmware, through its safety model, provides and enforces the +[openpilot safety](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md). Due to its critical function, it's important that the application code rigor within the `board` folder is held to high standards. + +These are the [CI regression tests](https://github.com/commaai/panda/actions) we have in place: +* A generic static code analysis is performed by [cppcheck](https://github.com/danmar/cppcheck/). +* In addition, [cppcheck](https://github.com/danmar/cppcheck/) has a specific addon to check for [MISRA C:2012](https://www.misra.org.uk/MISRAHome/MISRAC2012/tabid/196/Default.aspx) violations. See [current coverage](https://github.com/commaai/panda/blob/master/tests/misra/coverage_table). +* Compiler options are relatively strict: the flags `-Wall -Wextra -Wstrict-prototypes -Werror` are enforced. +* The [safety logic](https://github.com/commaai/panda/tree/master/board/safety) is tested and verified by [unit tests](https://github.com/commaai/panda/tree/master/tests/safety) for each supported car variant. +to ensure that the behavior remains unchanged. +* A hardware-in-the-loop test verifies panda's functionalities on all active panda variants, including: + * additional safety model checks + * compiling and flashing the bootstub and app code + * receiving, sending, and forwarding CAN messages on all buses + * CAN loopback and latency tests through USB and SPI + +The above tests are themselves tested by: +* a [mutation test](tests/misra/test_mutation.py) on the MISRA coverage +* 100% line coverage enforced on the safety unit tests + +In addition, we run the [ruff linter](https://github.com/astral-sh/ruff) and [mypy](https://mypy-lang.org/) on panda's Python library. + +## Usage + +Setup dependencies: +```bash +# Ubuntu +sudo apt-get install dfu-util gcc-arm-none-eabi python3-pip libffi-dev git + +# macOS +brew install --cask gcc-arm-embedded +brew install python3 dfu-util gcc@13 +``` + +Clone panda repository and install: +``` bash +git clone https://github.com/commaai/panda.git +cd panda + +# install dependencies +pip install -r requirements.txt + +# install panda +python setup.py install +``` + +See [the Panda class](https://github.com/commaai/panda/blob/master/python/__init__.py) for how to interact with the panda. + +For example, to receive CAN messages: +``` python +>>> from panda import Panda +>>> panda = Panda() +>>> panda.can_recv() +``` +And to send one on bus 0: +``` python +>>> panda.can_send(0x1aa, "message", 0) +``` +Note that you may have to setup [udev rules](https://github.com/commaai/panda/tree/master/drivers/linux) for Linux, such as +``` bash +sudo tee /etc/udev/rules.d/11-panda.rules < 0U); + tres_update_fan_ir_power(); + pwm_set(TIM3, 4, percentage); +} + +void tres_set_bootkick(BootState state) { + set_gpio_output(GPIOA, 0, state != BOOT_BOOTKICK); + set_gpio_output(GPIOC, 12, state != BOOT_RESET); +} + +void tres_set_fan_enabled(bool enabled) { + // NOTE: fan controller reset doesn't work on a tres if IR is enabled + tres_fan_enabled = enabled; + tres_update_fan_ir_power(); +} + +bool tres_read_som_gpio (void) { + return (get_gpio_input(GPIOC, 2) != 0); +} + +void tres_init(void) { + // Enable USB 3.3V LDO for USB block + register_set_bits(&(PWR->CR3), PWR_CR3_USBREGEN); + register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); + while ((PWR->CR3 & PWR_CR3_USB33RDY) == 0); + + red_chiplet_init(); + + // C2: SOM GPIO used as input (fan control at boot) + set_gpio_mode(GPIOC, 2, MODE_INPUT); + set_gpio_pullup(GPIOC, 2, PULL_DOWN); + + // SOM bootkick + reset lines + set_gpio_mode(GPIOC, 12, MODE_OUTPUT); + tres_set_bootkick(BOOT_BOOTKICK); + + // SOM debugging UART + gpio_uart7_init(); + uart_init(&uart_ring_som_debug, 115200); + + // SPI init + gpio_spi_init(); + + // fan setup + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + + // Initialize IR PWM and set to 0% + set_gpio_alternate(GPIOC, 9, GPIO_AF2_TIM3); + pwm_init(TIM3, 4); + tres_set_ir_power(0U); + + // Fake siren + set_gpio_alternate(GPIOC, 10, GPIO_AF4_I2C5); + set_gpio_alternate(GPIOC, 11, GPIO_AF4_I2C5); + register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11); // open drain + fake_siren_init(); + + // Clock source + clock_source_init(); +} + +const board board_tres = { + .harness_config = &red_chiplet_harness_config, + .has_obd = true, + .has_spi = true, + .has_canfd = true, + .fan_max_rpm = 6600U, + .avdd_mV = 1800U, + .fan_stall_recovery = false, + .fan_enable_cooldown_time = 3U, + .init = tres_init, + .init_bootloader = unused_init_bootloader, + .enable_can_transceiver = red_chiplet_enable_can_transceiver, + .enable_can_transceivers = red_chiplet_enable_can_transceivers, + .set_led = red_set_led, + .set_can_mode = red_chiplet_set_can_mode, + .check_ignition = red_check_ignition, + .read_voltage_mV = red_read_voltage_mV, + .read_current_mA = unused_read_current, + .set_fan_enabled = tres_set_fan_enabled, + .set_ir_power = tres_set_ir_power, + .set_siren = fake_siren_set, + .set_bootkick = tres_set_bootkick, + .read_som_gpio = tres_read_som_gpio +}; diff --git a/panda/board/boards/uno.h b/panda/board/boards/uno.h new file mode 100644 index 0000000..3c6ce17 --- /dev/null +++ b/panda/board/boards/uno.h @@ -0,0 +1,222 @@ +// /////////////////////// // +// Uno (STM32F4) + Harness // +// /////////////////////// // + +void uno_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ + case 1U: + set_gpio_output(GPIOC, 1, !enabled); + break; + case 2U: + set_gpio_output(GPIOC, 13, !enabled); + break; + case 3U: + set_gpio_output(GPIOA, 0, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 10, !enabled); + break; + default: + print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n"); + break; + } +} + +void uno_enable_can_transceivers(bool enabled) { + for(uint8_t i=1U; i<=4U; i++){ + // Leave main CAN always on for CAN-based ignition detection + if((harness.status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){ + uno_enable_can_transceiver(i, true); + } else { + uno_enable_can_transceiver(i, enabled); + } + } +} + +void uno_set_led(uint8_t color, bool enabled) { + switch (color){ + case LED_RED: + set_gpio_output(GPIOC, 9, !enabled); + break; + case LED_GREEN: + set_gpio_output(GPIOC, 7, !enabled); + break; + case LED_BLUE: + set_gpio_output(GPIOC, 6, !enabled); + break; + default: + break; + } +} + +void uno_set_bootkick(BootState state) { + if (state == BOOT_BOOTKICK) { + set_gpio_output(GPIOB, 14, false); + } else { + // We want the pin to be floating, not forced high! + set_gpio_mode(GPIOB, 14, MODE_INPUT); + } +} + +void uno_set_can_mode(uint8_t mode) { + uno_enable_can_transceiver(2U, false); + uno_enable_can_transceiver(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + case CAN_MODE_OBD_CAN2: + if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) { + // B12,B13: disable OBD mode + set_gpio_mode(GPIOB, 12, MODE_INPUT); + set_gpio_mode(GPIOB, 13, MODE_INPUT); + + // B5,B6: normal CAN2 mode + set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + uno_enable_can_transceiver(2U, true); + } else { + // B5,B6: disable normal CAN2 mode + set_gpio_mode(GPIOB, 5, MODE_INPUT); + set_gpio_mode(GPIOB, 6, MODE_INPUT); + + // B12,B13: OBD mode + set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + uno_enable_can_transceiver(4U, true); + } + break; + default: + print("Tried to set unsupported CAN mode: "); puth(mode); print("\n"); + break; + } +} + +bool uno_check_ignition(void){ + // ignition is checked through harness + return harness_check_ignition(); +} + +void uno_set_usb_switch(bool phone){ + set_gpio_output(GPIOB, 3, phone); +} + +void uno_set_ir_power(uint8_t percentage){ + pwm_set(TIM4, 2, percentage); +} + +void uno_set_fan_enabled(bool enabled){ + set_gpio_output(GPIOA, 1, enabled); +} + +void uno_init(void) { + common_init_gpio(); + + // A8,A15: normal CAN3 mode + set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); + + // C0: OBD_SBU1 (orientation detection) + // C3: OBD_SBU2 (orientation detection) + set_gpio_mode(GPIOC, 0, MODE_ANALOG); + set_gpio_mode(GPIOC, 3, MODE_ANALOG); + + // GPS off + set_gpio_output(GPIOB, 1, 0); + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 12, 0); + + // C10: OBD_SBU1_RELAY (harness relay driving output) + // C11: OBD_SBU2_RELAY (harness relay driving output) + set_gpio_mode(GPIOC, 10, MODE_OUTPUT); + set_gpio_mode(GPIOC, 11, MODE_OUTPUT); + set_gpio_output_type(GPIOC, 10, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output_type(GPIOC, 11, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output(GPIOC, 10, 1); + set_gpio_output(GPIOC, 11, 1); + + // C8: FAN PWM aka TIM3_CH3 + set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); + + // Turn on phone regulator + set_gpio_output(GPIOB, 4, true); + + // Initialize IR PWM and set to 0% + set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4); + pwm_init(TIM4, 2); + uno_set_ir_power(0U); + + // Initialize harness + harness_init(); + + + // Enable CAN transceivers + uno_enable_can_transceivers(true); + + // Disable LEDs + uno_set_led(LED_RED, false); + uno_set_led(LED_GREEN, false); + uno_set_led(LED_BLUE, false); + + // Set normal CAN mode + uno_set_can_mode(CAN_MODE_NORMAL); + + // change CAN mapping when flipped + if (harness.status == HARNESS_STATUS_FLIPPED) { + can_flip_buses(0, 2); + } + + // Switch to phone usb mode if harness connection is powered by less than 7V + if(white_read_voltage_mV() < 7000U){ + uno_set_usb_switch(true); + } else { + uno_set_usb_switch(false); + } + + // Bootkick phone + uno_set_bootkick(BOOT_BOOTKICK); +} + +void uno_init_bootloader(void) { + // GPS off + set_gpio_output(GPIOB, 1, 0); + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 12, 0); +} + +const harness_configuration uno_harness_config = { + .has_harness = true, + .GPIO_SBU1 = GPIOC, + .GPIO_SBU2 = GPIOC, + .GPIO_relay_SBU1 = GPIOC, + .GPIO_relay_SBU2 = GPIOC, + .pin_SBU1 = 0, + .pin_SBU2 = 3, + .pin_relay_SBU1 = 10, + .pin_relay_SBU2 = 11, + .adc_channel_SBU1 = 10, + .adc_channel_SBU2 = 13 +}; + +const board board_uno = { + .harness_config = &uno_harness_config, + .has_obd = true, + .has_spi = false, + .has_canfd = false, + .fan_max_rpm = 5100U, + .avdd_mV = 3300U, + .fan_stall_recovery = false, + .fan_enable_cooldown_time = 0U, + .init = uno_init, + .init_bootloader = uno_init_bootloader, + .enable_can_transceiver = uno_enable_can_transceiver, + .enable_can_transceivers = uno_enable_can_transceivers, + .set_led = uno_set_led, + .set_can_mode = uno_set_can_mode, + .check_ignition = uno_check_ignition, + .read_voltage_mV = white_read_voltage_mV, + .read_current_mA = unused_read_current, + .set_fan_enabled = uno_set_fan_enabled, + .set_ir_power = uno_set_ir_power, + .set_siren = unused_set_siren, + .set_bootkick = uno_set_bootkick, + .read_som_gpio = unused_read_som_gpio +}; diff --git a/panda/board/boards/unused_funcs.h b/panda/board/boards/unused_funcs.h new file mode 100644 index 0000000..7bfde01 --- /dev/null +++ b/panda/board/boards/unused_funcs.h @@ -0,0 +1,26 @@ +void unused_init_bootloader(void) { +} + +void unused_set_ir_power(uint8_t percentage) { + UNUSED(percentage); +} + +void unused_set_fan_enabled(bool enabled) { + UNUSED(enabled); +} + +void unused_set_siren(bool enabled) { + UNUSED(enabled); +} + +uint32_t unused_read_current(void) { + return 0U; +} + +void unused_set_bootkick(BootState state) { + UNUSED(state); +} + +bool unused_read_som_gpio(void) { + return false; +} \ No newline at end of file diff --git a/panda/board/boards/white.h b/panda/board/boards/white.h new file mode 100644 index 0000000..362f525 --- /dev/null +++ b/panda/board/boards/white.h @@ -0,0 +1,249 @@ +// ///////////////////// // +// White Panda (STM32F4) // +// ///////////////////// // + +void white_enable_can_transceiver(uint8_t transceiver, bool enabled) { + switch (transceiver){ + case 1U: + set_gpio_output(GPIOC, 1, !enabled); + break; + case 2U: + set_gpio_output(GPIOC, 13, !enabled); + break; + case 3U: + set_gpio_output(GPIOA, 0, !enabled); + break; + default: + print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n"); + break; + } +} + +void white_enable_can_transceivers(bool enabled) { + uint8_t t1 = enabled ? 1U : 2U; // leave transceiver 1 enabled to detect CAN ignition + for(uint8_t i=t1; i<=3U; i++) { + white_enable_can_transceiver(i, enabled); + } +} + +void white_set_led(uint8_t color, bool enabled) { + switch (color){ + case LED_RED: + set_gpio_output(GPIOC, 9, !enabled); + break; + case LED_GREEN: + set_gpio_output(GPIOC, 7, !enabled); + break; + case LED_BLUE: + set_gpio_output(GPIOC, 6, !enabled); + break; + default: + break; + } +} + +void white_set_usb_power_mode(uint8_t mode){ + switch (mode) { + case USB_POWER_CLIENT: + // B2,A13: set client mode + set_gpio_output(GPIOB, 2, 0); + set_gpio_output(GPIOA, 13, 1); + break; + case USB_POWER_CDP: + // B2,A13: set CDP mode + set_gpio_output(GPIOB, 2, 1); + set_gpio_output(GPIOA, 13, 1); + break; + case USB_POWER_DCP: + // B2,A13: set DCP mode on the charger (breaks USB!) + set_gpio_output(GPIOB, 2, 0); + set_gpio_output(GPIOA, 13, 0); + break; + default: + print("Invalid usb power mode\n"); + break; + } +} + +void white_set_can_mode(uint8_t mode){ + switch (mode) { + case CAN_MODE_NORMAL: + // B12,B13: disable GMLAN mode + set_gpio_mode(GPIOB, 12, MODE_INPUT); + set_gpio_mode(GPIOB, 13, MODE_INPUT); + + // B3,B4: disable GMLAN mode + set_gpio_mode(GPIOB, 3, MODE_INPUT); + set_gpio_mode(GPIOB, 4, MODE_INPUT); + + // B5,B6: normal CAN2 mode + set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + + // A8,A15: normal CAN3 mode + set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); + break; + case CAN_MODE_GMLAN_CAN2: + // B5,B6: disable CAN2 mode + set_gpio_mode(GPIOB, 5, MODE_INPUT); + set_gpio_mode(GPIOB, 6, MODE_INPUT); + + // B3,B4: disable GMLAN mode + set_gpio_mode(GPIOB, 3, MODE_INPUT); + set_gpio_mode(GPIOB, 4, MODE_INPUT); + + // B12,B13: GMLAN mode + set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + + // A8,A15: normal CAN3 mode + set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); + break; + case CAN_MODE_GMLAN_CAN3: + // A8,A15: disable CAN3 mode + set_gpio_mode(GPIOA, 8, MODE_INPUT); + set_gpio_mode(GPIOA, 15, MODE_INPUT); + + // B12,B13: disable GMLAN mode + set_gpio_mode(GPIOB, 12, MODE_INPUT); + set_gpio_mode(GPIOB, 13, MODE_INPUT); + + // B3,B4: GMLAN mode + set_gpio_alternate(GPIOB, 3, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOB, 4, GPIO_AF11_CAN3); + + // B5,B6: normal CAN2 mode + set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + break; + default: + print("Tried to set unsupported CAN mode: "); puth(mode); print("\n"); + break; + } +} + +uint32_t white_read_voltage_mV(void){ + return adc_get_mV(12) * 11U; +} + +uint32_t white_read_current_mA(void){ + // This isn't in mA, but we're keeping it for backwards compatibility + return adc_get_raw(13); +} + +bool white_check_ignition(void){ + // ignition is on PA1 + return !get_gpio_input(GPIOA, 1); +} + +void white_grey_init(void) { + common_init_gpio(); + + // C3: current sense + set_gpio_mode(GPIOC, 3, MODE_ANALOG); + + // A1: started_alt + set_gpio_pullup(GPIOA, 1, PULL_UP); + + // A2, A3: USART 2 for debugging + set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); + set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); + + // A4, A5, A6, A7: SPI + set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1); + + // B12: GMLAN, ignition sense, pull up + set_gpio_pullup(GPIOB, 12, PULL_UP); + + /* GMLAN mode pins: + M0(B15) M1(B14) mode + ======================= + 0 0 sleep + 1 0 100kbit + 0 1 high voltage wakeup + 1 1 33kbit (normal) + */ + set_gpio_output(GPIOB, 14, 1); + set_gpio_output(GPIOB, 15, 1); + + // B7: K-line enable + set_gpio_output(GPIOB, 7, 1); + + // C12, D2: Setup K-line (UART5) + set_gpio_alternate(GPIOC, 12, GPIO_AF8_UART5); + set_gpio_alternate(GPIOD, 2, GPIO_AF8_UART5); + set_gpio_pullup(GPIOD, 2, PULL_UP); + + // L-line enable + set_gpio_output(GPIOA, 14, 1); + + // C10, C11: L-Line setup (USART3) + set_gpio_alternate(GPIOC, 10, GPIO_AF7_USART3); + set_gpio_alternate(GPIOC, 11, GPIO_AF7_USART3); + set_gpio_pullup(GPIOC, 11, PULL_UP); + + + // Enable CAN transceivers + white_enable_can_transceivers(true); + + // Disable LEDs + white_set_led(LED_RED, false); + white_set_led(LED_GREEN, false); + white_set_led(LED_BLUE, false); + + // Set normal CAN mode + white_set_can_mode(CAN_MODE_NORMAL); + + // Init usb power mode + // Init in CDP mode only if panda is powered by 12V. + // Otherwise a PC would not be able to flash a standalone panda + if (white_read_voltage_mV() > 8000U) { // 8V threshold + white_set_usb_power_mode(USB_POWER_CDP); + } else { + white_set_usb_power_mode(USB_POWER_CLIENT); + } + + // ESP/GPS off + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 14, 0); +} + +void white_grey_init_bootloader(void) { + // ESP/GPS off + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 14, 0); +} + +const harness_configuration white_harness_config = { + .has_harness = false +}; + +const board board_white = { + .set_bootkick = unused_set_bootkick, + .harness_config = &white_harness_config, + .has_obd = false, + .has_spi = false, + .has_canfd = false, + .fan_max_rpm = 0U, + .avdd_mV = 3300U, + .fan_stall_recovery = false, + .fan_enable_cooldown_time = 0U, + .init = white_grey_init, + .init_bootloader = white_grey_init_bootloader, + .enable_can_transceiver = white_enable_can_transceiver, + .enable_can_transceivers = white_enable_can_transceivers, + .set_led = white_set_led, + .set_can_mode = white_set_can_mode, + .check_ignition = white_check_ignition, + .read_voltage_mV = white_read_voltage_mV, + .read_current_mA = white_read_current_mA, + .set_fan_enabled = unused_set_fan_enabled, + .set_ir_power = unused_set_ir_power, + .set_siren = unused_set_siren, + .read_som_gpio = unused_read_som_gpio +}; diff --git a/panda/board/bootstub_declarations.h b/panda/board/bootstub_declarations.h new file mode 100644 index 0000000..ae115d2 --- /dev/null +++ b/panda/board/bootstub_declarations.h @@ -0,0 +1,20 @@ +// ******************** Prototypes ******************** +void print(const char *a){ UNUSED(a); } +void puth(uint8_t i){ UNUSED(i); } +void puth2(uint8_t i){ UNUSED(i); } +void puth4(uint8_t i){ UNUSED(i); } +void hexdump(const void *a, int l){ UNUSED(a); UNUSED(l); } +typedef struct board board; +typedef struct harness_configuration harness_configuration; +// No CAN support on bootloader +void can_flip_buses(uint8_t bus1, uint8_t bus2){UNUSED(bus1); UNUSED(bus2);} +void pwm_init(TIM_TypeDef *TIM, uint8_t channel); +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); +// No UART support in bootloader +typedef struct uart_ring {} uart_ring; +uart_ring uart_ring_som_debug; +void uart_init(uart_ring *q, int baud) { UNUSED(q); UNUSED(baud); } + +// ********************* Globals ********************** +uint8_t hw_type = 0; +const board *current_board; diff --git a/panda/board/can_comms.h b/panda/board/can_comms.h new file mode 100644 index 0000000..56ca912 --- /dev/null +++ b/panda/board/can_comms.h @@ -0,0 +1,122 @@ +/* + CAN transactions to and from the host come in the form of + a certain number of CANPacket_t. The transaction is split + into multiple transfers or chunks. + + * comms_can_read outputs this buffer in chunks of a specified length. + chunks are always the given length, except the last one. + * comms_can_write reads in this buffer in chunks. + * both functions maintain an overflow buffer for a partial CANPacket_t that + spans multiple transfers/chunks. + * the overflow buffers are reset by a dedicated control transfer handler, + which is sent by the host on each start of a connection. +*/ + +typedef struct { + uint32_t ptr; + uint32_t tail_size; + uint8_t data[72]; +} asm_buffer; + +asm_buffer can_read_buffer = {.ptr = 0U, .tail_size = 0U}; + +int comms_can_read(uint8_t *data, uint32_t max_len) { + uint32_t pos = 0U; + + // Send tail of previous message if it is in buffer + if (can_read_buffer.ptr > 0U) { + uint32_t overflow_len = MIN(max_len - pos, can_read_buffer.ptr); + (void)memcpy(&data[pos], can_read_buffer.data, overflow_len); + pos += overflow_len; + (void)memcpy(can_read_buffer.data, &can_read_buffer.data[overflow_len], can_read_buffer.ptr - overflow_len); + can_read_buffer.ptr -= overflow_len; + } + + if (can_read_buffer.ptr == 0U) { + // Fill rest of buffer with new data + CANPacket_t can_packet; + while ((pos < max_len) && can_pop(&can_rx_q, &can_packet)) { + uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code]; + if ((pos + pckt_len) <= max_len) { + (void)memcpy(&data[pos], &can_packet, pckt_len); + pos += pckt_len; + } else { + (void)memcpy(&data[pos], &can_packet, max_len - pos); + can_read_buffer.ptr += pckt_len - (max_len - pos); + // cppcheck-suppress objectIndex + (void)memcpy(can_read_buffer.data, &((uint8_t*)&can_packet)[(max_len - pos)], can_read_buffer.ptr); + pos = max_len; + } + } + } + + return pos; +} + +asm_buffer can_write_buffer = {.ptr = 0U, .tail_size = 0U}; + +// send on CAN +void comms_can_write(const uint8_t *data, uint32_t len) { + uint32_t pos = 0U; + + // Assembling can message with data from buffer + if (can_write_buffer.ptr != 0U) { + if (can_write_buffer.tail_size <= (len - pos)) { + // we have enough data to complete the buffer + CANPacket_t to_push; + (void)memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], can_write_buffer.tail_size); + can_write_buffer.ptr += can_write_buffer.tail_size; + pos += can_write_buffer.tail_size; + + // send out + (void)memcpy(&to_push, can_write_buffer.data, can_write_buffer.ptr); + can_send(&to_push, to_push.bus, false); + + // reset overflow buffer + can_write_buffer.ptr = 0U; + can_write_buffer.tail_size = 0U; + } else { + // maybe next time + uint32_t data_size = len - pos; + (void) memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], data_size); + can_write_buffer.tail_size -= data_size; + can_write_buffer.ptr += data_size; + pos += data_size; + } + } + + // rest of the message + while (pos < len) { + uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(data[pos] >> 4U)]; + if ((pos + pckt_len) <= len) { + CANPacket_t to_push; + (void)memcpy(&to_push, &data[pos], pckt_len); + can_send(&to_push, to_push.bus, false); + pos += pckt_len; + } else { + (void)memcpy(can_write_buffer.data, &data[pos], len - pos); + can_write_buffer.ptr = len - pos; + can_write_buffer.tail_size = pckt_len - can_write_buffer.ptr; + pos += can_write_buffer.ptr; + } + } + + refresh_can_tx_slots_available(); +} + +void comms_can_reset(void) { + can_write_buffer.ptr = 0U; + can_write_buffer.tail_size = 0U; + can_read_buffer.ptr = 0U; + can_read_buffer.tail_size = 0U; +} + +// TODO: make this more general! +void refresh_can_tx_slots_available(void) { + if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_USB_BULK_TRANSFER)) { + can_tx_comms_resume_usb(); + } + if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER)) { + can_tx_comms_resume_spi(); + } +} diff --git a/panda/board/can_definitions.h b/panda/board/can_definitions.h new file mode 100644 index 0000000..b3631d8 --- /dev/null +++ b/panda/board/can_definitions.h @@ -0,0 +1,34 @@ +#pragma once + +const uint8_t PANDA_CAN_CNT = 3U; +const uint8_t PANDA_BUS_CNT = 4U; + +// bump this when changing the CAN packet +#define CAN_PACKET_VERSION 4 + +#define CANPACKET_HEAD_SIZE 6U + +#if !defined(STM32F4) + #define CANFD + #define CANPACKET_DATA_SIZE_MAX 64U +#else + #define CANPACKET_DATA_SIZE_MAX 8U +#endif + +typedef struct { + unsigned char reserved : 1; + unsigned char bus : 3; + unsigned char data_len_code : 4; // lookup length with dlc_to_len + unsigned char rejected : 1; + unsigned char returned : 1; + unsigned char extended : 1; + unsigned int addr : 29; + unsigned char checksum; + unsigned char data[CANPACKET_DATA_SIZE_MAX]; +} __attribute__((packed, aligned(4))) CANPacket_t; + +const unsigned char dlc_to_len[] = {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 12U, 16U, 20U, 24U, 32U, 48U, 64U}; + +#define GET_BUS(msg) ((msg)->bus) +#define GET_LEN(msg) (dlc_to_len[(msg)->data_len_code]) +#define GET_ADDR(msg) ((msg)->addr) diff --git a/panda/board/comms_definitions.h b/panda/board/comms_definitions.h new file mode 100644 index 0000000..18a6d2f --- /dev/null +++ b/panda/board/comms_definitions.h @@ -0,0 +1,12 @@ +typedef struct { + uint8_t request; + uint16_t param1; + uint16_t param2; + uint16_t length; +} __attribute__((packed)) ControlPacket_t; + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp); +void comms_endpoint2_write(const uint8_t *data, uint32_t len); +void comms_can_write(const uint8_t *data, uint32_t len); +int comms_can_read(uint8_t *data, uint32_t max_len); +void comms_can_reset(void); diff --git a/panda/board/config.h b/panda/board/config.h new file mode 100644 index 0000000..5670352 --- /dev/null +++ b/panda/board/config.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +//#define DEBUG +//#define DEBUG_UART +//#define DEBUG_USB +//#define DEBUG_SPI +//#define DEBUG_FAULTS +//#define DEBUG_COMMS +//#define DEBUG_FAN + +#define CAN_INIT_TIMEOUT_MS 500U +#define DEEPSLEEP_WAKEUP_DELAY 3U +#define USBPACKET_MAX_SIZE 0x40U +#define MAX_CAN_MSGS_PER_USB_BULK_TRANSFER 51U +#define MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER 170U + +// USB definitions +#define USB_VID 0xBBAAU + +#ifdef PANDA_JUNGLE + #ifdef BOOTSTUB + #define USB_PID 0xDDEFU + #else + #define USB_PID 0xDDCFU + #endif +#else + #ifdef BOOTSTUB + #define USB_PID 0xDDEEU + #else + #define USB_PID 0xDDCCU + #endif +#endif + +// platform includes +#ifdef STM32H7 + #include "stm32h7/stm32h7_config.h" +#elif defined(STM32F4) + #include "stm32f4/stm32f4_config.h" +#else + // TODO: uncomment this, cppcheck complains + // building for tests + //#include "fake_stm.h" +#endif diff --git a/panda/board/crc.h b/panda/board/crc.h new file mode 100644 index 0000000..3e20fb0 --- /dev/null +++ b/panda/board/crc.h @@ -0,0 +1,19 @@ +#pragma once + +uint8_t crc_checksum(const uint8_t *dat, int len, const uint8_t poly) { + uint8_t crc = 0xFFU; + int i; + int j; + for (i = len - 1; i >= 0; i--) { + crc ^= dat[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80U) != 0U) { + crc = (uint8_t)((crc << 1) ^ poly); + } + else { + crc <<= 1; + } + } + } + return crc; +} diff --git a/panda/board/critical.h b/panda/board/critical.h new file mode 100644 index 0000000..c8cf52c --- /dev/null +++ b/panda/board/critical.h @@ -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(); \ + } diff --git a/panda/board/drivers/bootkick.h b/panda/board/drivers/bootkick.h new file mode 100644 index 0000000..acae361 --- /dev/null +++ b/panda/board/drivers/bootkick.h @@ -0,0 +1,67 @@ +bool bootkick_ign_prev = false; +BootState boot_state = BOOT_BOOTKICK; +uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC; + +uint8_t boot_reset_countdown = 0; +uint8_t waiting_to_boot_countdown = 0; +bool bootkick_reset_triggered = false; +uint16_t bootkick_last_serial_ptr = 0; + +void bootkick_tick(bool ignition, bool recent_heartbeat) { + BootState boot_state_prev = boot_state; + const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC); + + if ((ignition && !bootkick_ign_prev) || harness_inserted) { + // bootkick on rising edge of ignition or harness insertion + boot_state = BOOT_BOOTKICK; + } else if (recent_heartbeat) { + // disable bootkick once openpilot is up + boot_state = BOOT_STANDBY; + } else { + + } + + /* + Ensure SOM boots in case it goes into QDL mode. Reset behavior: + * shouldn't trigger on the first boot after power-on + * only try reset once per bootkick, i.e. don't keep trying until booted + * only try once per panda boot, since openpilot will reset panda on startup + * once BOOT_RESET is triggered, it stays until countdown is finished + */ + if (!bootkick_reset_triggered && (boot_state == BOOT_BOOTKICK) && (boot_state_prev == BOOT_STANDBY)) { + waiting_to_boot_countdown = 45U; + } + if (waiting_to_boot_countdown > 0U) { + bool serial_activity = uart_ring_som_debug.w_ptr_tx != bootkick_last_serial_ptr; + if (serial_activity || current_board->read_som_gpio() || (boot_state != BOOT_BOOTKICK)) { + waiting_to_boot_countdown = 0U; + } else { + // try a reset + if (waiting_to_boot_countdown == 1U) { + boot_reset_countdown = 5U; + } + } + } + + // handle reset state + if (boot_reset_countdown > 0U) { + boot_state = BOOT_RESET; + bootkick_reset_triggered = true; + } else { + if (boot_state == BOOT_RESET) { + boot_state = BOOT_BOOTKICK; + } + } + + // update state + bootkick_ign_prev = ignition; + bootkick_harness_status_prev = harness.status; + bootkick_last_serial_ptr = uart_ring_som_debug.w_ptr_tx; + if (waiting_to_boot_countdown > 0U) { + waiting_to_boot_countdown--; + } + if (boot_reset_countdown > 0U) { + boot_reset_countdown--; + } + current_board->set_bootkick(boot_state); +} diff --git a/panda/board/drivers/bxcan.h b/panda/board/drivers/bxcan.h new file mode 100644 index 0000000..ea2705d --- /dev/null +++ b/panda/board/drivers/bxcan.h @@ -0,0 +1,213 @@ +// IRQs: CAN1_TX, CAN1_RX0, CAN1_SCE +// CAN2_TX, CAN2_RX0, CAN2_SCE +// CAN3_TX, CAN3_RX0, CAN3_SCE + +CAN_TypeDef *cans[] = {CAN1, CAN2, CAN3}; +uint8_t can_irq_number[3][3] = { + { CAN1_TX_IRQn, CAN1_RX0_IRQn, CAN1_SCE_IRQn }, + { CAN2_TX_IRQn, CAN2_RX0_IRQn, CAN2_SCE_IRQn }, + { CAN3_TX_IRQn, CAN3_RX0_IRQn, CAN3_SCE_IRQn }, +}; + +bool can_set_speed(uint8_t can_number) { + bool ret = true; + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + ret &= llcan_set_speed( + CANx, + bus_config[bus_number].can_speed, + can_loopback, + (unsigned int)(can_silent) & (1U << can_number) + ); + return ret; +} + +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + uint32_t esr_reg = CANx->ESR; + + can_health[can_number].bus_off = ((esr_reg & CAN_ESR_BOFF) >> CAN_ESR_BOFF_Pos); + can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; + can_health[can_number].error_warning = ((esr_reg & CAN_ESR_EWGF) >> CAN_ESR_EWGF_Pos); + can_health[can_number].error_passive = ((esr_reg & CAN_ESR_EPVF) >> CAN_ESR_EPVF_Pos); + + can_health[can_number].last_error = ((esr_reg & CAN_ESR_LEC) >> CAN_ESR_LEC_Pos); + if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { + can_health[can_number].last_stored_error = can_health[can_number].last_error; + } + + can_health[can_number].receive_error_cnt = ((esr_reg & CAN_ESR_REC) >> CAN_ESR_REC_Pos); + can_health[can_number].transmit_error_cnt = ((esr_reg & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos); + + can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; + can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; + can_health[can_number].irq2_call_rate = interrupts[can_irq_number[can_number][2]].call_rate; + + if (ir_reg != 0U) { + can_health[can_number].total_error_cnt += 1U; + + // RX message lost due to FIFO overrun + if ((CANx->RF0R & (CAN_RF0R_FOVR0)) != 0) { + can_health[can_number].total_rx_lost_cnt += 1U; + CANx->RF0R &= ~(CAN_RF0R_FOVR0); + } + can_health[can_number].can_core_reset_cnt += 1U; + llcan_clear_send(CANx); + } +} + +// ***************************** CAN ***************************** +// CANx_SCE IRQ Handler +void can_sce(uint8_t can_number) { + update_can_health_pkt(can_number, 1U); +} + +// CANx_TX IRQ Handler +void process_can(uint8_t can_number) { + if (can_number != 0xffU) { + + ENTER_CRITICAL(); + + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + // check for empty mailbox + CANPacket_t to_send; + if ((CANx->TSR & (CAN_TSR_TERR0 | CAN_TSR_ALST0)) != 0) { // last TX failed due to error arbitration lost + can_health[can_number].total_tx_lost_cnt += 1U; + CANx->TSR |= (CAN_TSR_TERR0 | CAN_TSR_ALST0); + } + if ((CANx->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { + // add successfully transmitted message to my fifo + if ((CANx->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) { + if ((CANx->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) { + CANPacket_t to_push; + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = (CANx->sTxMailBox[0].TIR >> 2) & 0x1U; + to_push.addr = (to_push.extended != 0U) ? (CANx->sTxMailBox[0].TIR >> 3) : (CANx->sTxMailBox[0].TIR >> 21); + to_push.data_len_code = CANx->sTxMailBox[0].TDTR & 0xFU; + to_push.bus = bus_number; + WORD_TO_BYTE_ARRAY(&to_push.data[0], CANx->sTxMailBox[0].TDLR); + WORD_TO_BYTE_ARRAY(&to_push.data[4], CANx->sTxMailBox[0].TDHR); + can_set_checksum(&to_push); + + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + } + + // clear interrupt + // careful, this can also be cleared by requesting a transmission + CANx->TSR |= CAN_TSR_RQCP0; + } + + if (can_pop(can_queues[bus_number], &to_send)) { + if (can_check_checksum(&to_send)) { + can_health[can_number].total_tx_cnt += 1U; + // only send if we have received a packet + CANx->sTxMailBox[0].TIR = ((to_send.extended != 0U) ? (to_send.addr << 3) : (to_send.addr << 21)) | (to_send.extended << 2); + CANx->sTxMailBox[0].TDTR = to_send.data_len_code; + BYTE_ARRAY_TO_WORD(CANx->sTxMailBox[0].TDLR, &to_send.data[0]); + BYTE_ARRAY_TO_WORD(CANx->sTxMailBox[0].TDHR, &to_send.data[4]); + // Send request TXRQ + CANx->sTxMailBox[0].TIR |= 0x1U; + } else { + can_health[can_number].total_tx_checksum_error_cnt += 1U; + } + + refresh_can_tx_slots_available(); + } + } + + EXIT_CRITICAL(); + } +} + +// CANx_RX0 IRQ Handler +// blink blue when we are receiving CAN messages +void can_rx(uint8_t can_number) { + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + while ((CANx->RF0R & CAN_RF0R_FMP0) != 0) { + can_health[can_number].total_rx_cnt += 1U; + + // can is live + pending_can_live = 1; + + // add to my fifo + CANPacket_t to_push; + + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (CANx->sFIFOMailBox[0].RIR >> 2) & 0x1U; + to_push.addr = (to_push.extended != 0U) ? (CANx->sFIFOMailBox[0].RIR >> 3) : (CANx->sFIFOMailBox[0].RIR >> 21); + to_push.data_len_code = CANx->sFIFOMailBox[0].RDTR & 0xFU; + to_push.bus = bus_number; + WORD_TO_BYTE_ARRAY(&to_push.data[0], CANx->sFIFOMailBox[0].RDLR); + WORD_TO_BYTE_ARRAY(&to_push.data[4], CANx->sFIFOMailBox[0].RDHR); + can_set_checksum(&to_push); + + // forwarding (panda only) + int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); + if (bus_fwd_num != -1) { + CANPacket_t to_send; + + to_send.returned = 0U; + to_send.rejected = 0U; + to_send.extended = to_push.extended; // TXRQ + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_send); + + can_send(&to_send, bus_fwd_num, true); + can_health[can_number].total_fwd_cnt += 1U; + } + + safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; + ignition_can_hook(&to_push); + + current_board->set_led(LED_BLUE, true); + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + + // next + CANx->RF0R |= CAN_RF0R_RFOM0; + } +} + +void CAN1_TX_IRQ_Handler(void) { process_can(0); } +void CAN1_RX0_IRQ_Handler(void) { can_rx(0); } +void CAN1_SCE_IRQ_Handler(void) { can_sce(0); } + +void CAN2_TX_IRQ_Handler(void) { process_can(1); } +void CAN2_RX0_IRQ_Handler(void) { can_rx(1); } +void CAN2_SCE_IRQ_Handler(void) { can_sce(1); } + +void CAN3_TX_IRQ_Handler(void) { process_can(2); } +void CAN3_RX0_IRQ_Handler(void) { can_rx(2); } +void CAN3_SCE_IRQ_Handler(void) { can_sce(2); } + +bool can_init(uint8_t can_number) { + bool ret = false; + + REGISTER_INTERRUPT(CAN1_TX_IRQn, CAN1_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(CAN1_RX0_IRQn, CAN1_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(CAN1_SCE_IRQn, CAN1_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(CAN2_TX_IRQn, CAN2_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(CAN2_RX0_IRQn, CAN2_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(CAN2_SCE_IRQn, CAN2_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(CAN3_TX_IRQn, CAN3_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + REGISTER_INTERRUPT(CAN3_RX0_IRQn, CAN3_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + REGISTER_INTERRUPT(CAN3_SCE_IRQn, CAN3_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + + if (can_number != 0xffU) { + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + ret &= can_set_speed(can_number); + ret &= llcan_init(CANx); + // in case there are queued up messages + process_can(can_number); + } + return ret; +} diff --git a/panda/board/drivers/can_common.h b/panda/board/drivers/can_common.h new file mode 100644 index 0000000..ed611b5 --- /dev/null +++ b/panda/board/drivers/can_common.h @@ -0,0 +1,295 @@ +typedef struct { + volatile uint32_t w_ptr; + volatile uint32_t r_ptr; + uint32_t fifo_size; + CANPacket_t *elems; +} can_ring; + +typedef struct { + uint8_t bus_lookup; + uint8_t can_num_lookup; + int8_t forwarding_bus; + uint32_t can_speed; + uint32_t can_data_speed; + bool canfd_enabled; + bool brs_enabled; + bool canfd_non_iso; +} bus_config_t; + +uint32_t safety_tx_blocked = 0; +uint32_t safety_rx_invalid = 0; +uint32_t tx_buffer_overflow = 0; +uint32_t rx_buffer_overflow = 0; +uint32_t gmlan_send_errs = 0; + +can_health_t can_health[] = {{0}, {0}, {0}}; + +extern int can_live; +extern int pending_can_live; + +// must reinit after changing these +extern int can_silent; +extern bool can_loopback; + +// Ignition detected from CAN meessages +bool ignition_can = false; +uint32_t ignition_can_cnt = 0U; + +#define ALL_CAN_SILENT 0xFF +#define ALL_CAN_LIVE 0 + +int can_live = 0; +int pending_can_live = 0; +int can_silent = ALL_CAN_SILENT; +bool can_loopback = false; + +// ******************* functions prototypes ********************* +bool can_init(uint8_t can_number); +void process_can(uint8_t can_number); + +// ********************* instantiate queues ********************* +#define can_buffer(x, size) \ + CANPacket_t elems_##x[size]; \ + can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CANPacket_t *)&(elems_##x) }; + +#define CAN_RX_BUFFER_SIZE 4096U +#define CAN_TX_BUFFER_SIZE 416U +#define GMLAN_TX_BUFFER_SIZE 416U + +#ifdef STM32H7 +// ITCM RAM and DTCM RAM are the fastest for Cortex-M7 core access +__attribute__((section(".axisram"))) can_buffer(rx_q, CAN_RX_BUFFER_SIZE) +__attribute__((section(".itcmram"))) can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) +__attribute__((section(".itcmram"))) can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) +#else +can_buffer(rx_q, CAN_RX_BUFFER_SIZE) +can_buffer(tx1_q, CAN_TX_BUFFER_SIZE) +can_buffer(tx2_q, CAN_TX_BUFFER_SIZE) +#endif +can_buffer(tx3_q, CAN_TX_BUFFER_SIZE) +can_buffer(txgmlan_q, GMLAN_TX_BUFFER_SIZE) +// FIXME: +// cppcheck-suppress misra-c2012-9.3 +can_ring *can_queues[] = {&can_tx1_q, &can_tx2_q, &can_tx3_q, &can_txgmlan_q}; + +// helpers +#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFFU); 1[dst8] = (((src32) >> 8U) & 0xFFU); 2[dst8] = (((src32) >> 16U) & 0xFFU); 3[dst8] = (((src32) >> 24U) & 0xFFU) +#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8U) | (2[src8] << 16U) | (3[src8] << 24U)) + +// ********************* interrupt safe queue ********************* +bool can_pop(can_ring *q, CANPacket_t *elem) { + bool ret = 0; + + ENTER_CRITICAL(); + if (q->w_ptr != q->r_ptr) { + *elem = q->elems[q->r_ptr]; + if ((q->r_ptr + 1U) == q->fifo_size) { + q->r_ptr = 0; + } else { + q->r_ptr += 1U; + } + ret = 1; + } + EXIT_CRITICAL(); + + return ret; +} + +bool can_push(can_ring *q, const CANPacket_t *elem) { + bool ret = false; + uint32_t next_w_ptr; + + ENTER_CRITICAL(); + if ((q->w_ptr + 1U) == q->fifo_size) { + next_w_ptr = 0; + } else { + next_w_ptr = q->w_ptr + 1U; + } + if (next_w_ptr != q->r_ptr) { + q->elems[q->w_ptr] = *elem; + q->w_ptr = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + if (!ret) { + #ifdef DEBUG + print("can_push to "); + if (q == &can_rx_q) { + print("can_rx_q"); + } else if (q == &can_tx1_q) { + print("can_tx1_q"); + } else if (q == &can_tx2_q) { + print("can_tx2_q"); + } else if (q == &can_tx3_q) { + print("can_tx3_q"); + } else if (q == &can_txgmlan_q) { + print("can_txgmlan_q"); + } else { + print("unknown"); + } + print(" failed!\n"); + #endif + } + return ret; +} + +uint32_t can_slots_empty(const can_ring *q) { + uint32_t ret = 0; + + ENTER_CRITICAL(); + if (q->w_ptr >= q->r_ptr) { + ret = q->fifo_size - 1U - q->w_ptr + q->r_ptr; + } else { + ret = q->r_ptr - q->w_ptr - 1U; + } + EXIT_CRITICAL(); + + return ret; +} + +void can_clear(can_ring *q) { + ENTER_CRITICAL(); + q->w_ptr = 0; + q->r_ptr = 0; + EXIT_CRITICAL(); + // handle TX buffer full with zero ECUs awake on the bus + refresh_can_tx_slots_available(); +} + +// assign CAN numbering +// bus num: Can bus number on ODB connector. Sent to/from USB +// Min: 0; Max: 127; Bit 7 marks message as receipt (bus 129 is receipt for but 1) +// cans: Look up MCU can interface from bus number +// can number: numeric lookup for MCU CAN interfaces (0 = CAN1, 1 = CAN2, etc); +// bus_lookup: Translates from 'can number' to 'bus number'. +// can_num_lookup: Translates from 'bus number' to 'can number'. +// forwarding bus: If >= 0, forward all messages from this bus to the specified bus. + +// Helpers +// Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3 +bus_config_t bus_config[] = { + { .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .bus_lookup = 1U, .can_num_lookup = 1U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .bus_lookup = 2U, .can_num_lookup = 2U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .bus_lookup = 0xFFU, .can_num_lookup = 0xFFU, .forwarding_bus = -1, .can_speed = 333U, .can_data_speed = 333U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, +}; + +#define CANIF_FROM_CAN_NUM(num) (cans[num]) +#define BUS_NUM_FROM_CAN_NUM(num) (bus_config[num].bus_lookup) +#define CAN_NUM_FROM_BUS_NUM(num) (bus_config[num].can_num_lookup) + +void can_init_all(void) { + bool ret = true; + for (uint8_t i=0U; i < PANDA_CAN_CNT; i++) { + if (!current_board->has_canfd) { + bus_config[i].can_data_speed = 0U; + } + can_clear(can_queues[i]); + ret &= can_init(i); + } + UNUSED(ret); +} + +void can_flip_buses(uint8_t bus1, uint8_t bus2){ + bus_config[bus1].bus_lookup = bus2; + bus_config[bus2].bus_lookup = bus1; + bus_config[bus1].can_num_lookup = bus2; + bus_config[bus2].can_num_lookup = bus1; +} + +void can_set_forwarding(uint8_t from, uint8_t to) { + bus_config[from].forwarding_bus = to; +} + +void ignition_can_hook(CANPacket_t *to_push) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + + if (bus == 0) { + // GM exception + if ((addr == 0x1F1) && (len == 8)) { + // SystemPowerMode (2=Run, 3=Crank Request) + ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U; + ignition_can_cnt = 0U; + } + + // Tesla exception + if ((addr == 0x348) && (len == 8)) { + // GTW_status + ignition_can = (GET_BYTE(to_push, 0) & 0x1U) != 0U; + ignition_can_cnt = 0U; + } + + // Mazda exception + if ((addr == 0x9E) && (len == 8)) { + ignition_can = (GET_BYTE(to_push, 0) >> 5) == 0x6U; + ignition_can_cnt = 0U; + } + + } else if (bus == 2) { + // GM exception, SDGM cars have this message on bus 2 + if ((addr == 0x1F1) && (len == 8)) { + // SystemPowerMode (2=Run, 3=Crank Request) + ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U; + ignition_can_cnt = 0U; + } + } +} + +bool can_tx_check_min_slots_free(uint32_t min) { + return + (can_slots_empty(&can_tx1_q) >= min) && + (can_slots_empty(&can_tx2_q) >= min) && + (can_slots_empty(&can_tx3_q) >= min) && + (can_slots_empty(&can_txgmlan_q) >= min); +} + +uint8_t calculate_checksum(const uint8_t *dat, uint32_t len) { + uint8_t checksum = 0U; + for (uint32_t i = 0U; i < len; i++) { + checksum ^= dat[i]; + } + return checksum; +} + +void can_set_checksum(CANPacket_t *packet) { + packet->checksum = 0U; + packet->checksum = calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)); +} + +bool can_check_checksum(CANPacket_t *packet) { + return (calculate_checksum((uint8_t *) packet, CANPACKET_HEAD_SIZE + GET_LEN(packet)) == 0U); +} + +void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) { + if (skip_tx_hook || safety_tx_hook(to_push) != 0) { + if (bus_number < PANDA_BUS_CNT) { + // add CAN packet to send queue + if ((bus_number == 3U) && (bus_config[3].can_num_lookup == 0xFFU)) { + gmlan_send_errs += bitbang_gmlan(to_push) ? 0U : 1U; + } else { + tx_buffer_overflow += can_push(can_queues[bus_number], to_push) ? 0U : 1U; + process_can(CAN_NUM_FROM_BUS_NUM(bus_number)); + } + } + } else { + safety_tx_blocked += 1U; + to_push->returned = 0U; + to_push->rejected = 1U; + + // data changed + can_set_checksum(to_push); + rx_buffer_overflow += can_push(&can_rx_q, to_push) ? 0U : 1U; + } +} + +bool is_speed_valid(uint32_t speed, const uint32_t *all_speeds, uint8_t len) { + bool ret = false; + for (uint8_t i = 0U; i < len; i++) { + if (all_speeds[i] == speed) { + ret = true; + } + } + return ret; +} diff --git a/panda/board/drivers/clock_source.h b/panda/board/drivers/clock_source.h new file mode 100644 index 0000000..11b2fa3 --- /dev/null +++ b/panda/board/drivers/clock_source.h @@ -0,0 +1,37 @@ +#define CLOCK_SOURCE_PERIOD_MS 50U +#define CLOCK_SOURCE_PULSE_LEN_MS 2U + +void clock_source_set_period(uint8_t period) { + register_set(&(TIM1->ARR), ((period*10U) - 1U), 0xFFFFU); +} + +void clock_source_init(void) { + // Setup timer + register_set(&(TIM1->PSC), ((APB2_TIMER_FREQ*100U)-1U), 0xFFFFU); // Tick on 0.1 ms + register_set(&(TIM1->ARR), ((CLOCK_SOURCE_PERIOD_MS*10U) - 1U), 0xFFFFU); // Period + register_set(&(TIM1->CCMR1), 0U, 0xFFFFU); // No output on compare + register_set(&(TIM1->CCER), TIM_CCER_CC1E, 0xFFFFU); // Enable compare 1 + register_set(&(TIM1->CCR1), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set(&(TIM1->CCR2), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set(&(TIM1->CCR3), (CLOCK_SOURCE_PULSE_LEN_MS*10U), 0xFFFFU); // Compare 1 value + register_set_bits(&(TIM1->DIER), TIM_DIER_UIE | TIM_DIER_CC1IE); // Enable interrupts + register_set(&(TIM1->CR1), TIM_CR1_CEN, 0x3FU); // Enable timer + + // No interrupts + NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn); + NVIC_DisableIRQ(TIM1_CC_IRQn); + + // Set GPIO as timer channels + set_gpio_alternate(GPIOB, 14, GPIO_AF1_TIM1); + set_gpio_alternate(GPIOB, 15, GPIO_AF1_TIM1); + + // Set PWM mode + register_set(&(TIM1->CCMR1), (0b110 << TIM_CCMR1_OC2M_Pos), 0xFFFFU); + register_set(&(TIM1->CCMR2), (0b110 << TIM_CCMR2_OC3M_Pos), 0xFFFFU); + + // Enable output + register_set(&(TIM1->BDTR), TIM_BDTR_MOE, 0xFFFFU); + + // Enable complementary compares + register_set_bits(&(TIM1->CCER), TIM_CCER_CC2NE | TIM_CCER_CC3NE); +} diff --git a/panda/board/drivers/fake_siren.h b/panda/board/drivers/fake_siren.h new file mode 100644 index 0000000..38c87de --- /dev/null +++ b/panda/board/drivers/fake_siren.h @@ -0,0 +1,76 @@ +#include "stm32h7/lli2c.h" + +#define CODEC_I2C_ADDR 0x10 + +// 1Vpp sine wave with 1V offset +const uint8_t fake_siren_lut[360] = { 134U, 135U, 137U, 138U, 139U, 140U, 141U, 143U, 144U, 145U, 146U, 148U, 149U, 150U, 151U, 152U, 154U, 155U, 156U, 157U, 158U, 159U, 160U, 162U, 163U, 164U, 165U, 166U, 167U, 168U, 169U, 170U, 171U, 172U, 174U, 175U, 176U, 177U, 177U, 178U, 179U, 180U, 181U, 182U, 183U, 184U, 185U, 186U, 186U, 187U, 188U, 189U, 190U, 190U, 191U, 192U, 193U, 193U, 194U, 195U, 195U, 196U, 196U, 197U, 197U, 198U, 199U, 199U, 199U, 200U, 200U, 201U, 201U, 202U, 202U, 202U, 203U, 203U, 203U, 203U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 205U, 205U, 205U, 205U, 205U, 205U, 205U, 204U, 204U, 204U, 204U, 204U, 204U, 204U, 203U, 203U, 203U, 203U, 202U, 202U, 202U, 201U, 201U, 200U, 200U, 199U, 199U, 199U, 198U, 197U, 197U, 196U, 196U, 195U, 195U, 194U, 193U, 193U, 192U, 191U, 190U, 190U, 189U, 188U, 187U, 186U, 186U, 185U, 184U, 183U, 182U, 181U, 180U, 179U, 178U, 177U, 177U, 176U, 175U, 174U, 172U, 171U, 170U, 169U, 168U, 167U, 166U, 165U, 164U, 163U, 162U, 160U, 159U, 158U, 157U, 156U, 155U, 154U, 152U, 151U, 150U, 149U, 148U, 146U, 145U, 144U, 143U, 141U, 140U, 139U, 138U, 137U, 135U, 134U, 133U, 132U, 130U, 129U, 128U, 127U, 125U, 124U, 123U, 122U, 121U, 119U, 118U, 117U, 116U, 115U, 113U, 112U, 111U, 110U, 109U, 108U, 106U, 105U, 104U, 103U, 102U, 101U, 100U, 99U, 98U, 97U, 96U, 95U, 94U, 93U, 92U, 91U, 90U, 89U, 88U, 87U, 86U, 85U, 84U, 83U, 82U, 82U, 81U, 80U, 79U, 78U, 78U, 77U, 76U, 76U, 75U, 74U, 74U, 73U, 72U, 72U, 71U, 71U, 70U, 70U, 69U, 69U, 68U, 68U, 67U, 67U, 67U, 66U, 66U, 66U, 65U, 65U, 65U, 65U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 63U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 64U, 65U, 65U, 65U, 65U, 66U, 66U, 66U, 67U, 67U, 67U, 68U, 68U, 69U, 69U, 70U, 70U, 71U, 71U, 72U, 72U, 73U, 74U, 74U, 75U, 76U, 76U, 77U, 78U, 78U, 79U, 80U, 81U, 82U, 82U, 83U, 84U, 85U, 86U, 87U, 88U, 89U, 90U, 91U, 92U, 93U, 94U, 95U, 96U, 97U, 98U, 99U, 100U, 101U, 102U, 103U, 104U, 105U, 106U, 108U, 109U, 110U, 111U, 112U, 113U, 115U, 116U, 117U, 118U, 119U, 121U, 122U, 123U, 124U, 125U, 127U, 128U, 129U, 130U, 132U, 133U }; + +bool fake_siren_enabled = false; + +void fake_siren_codec_enable(bool enabled) { + if (enabled) { + bool success = true; + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2B, (1U << 1)); // Left speaker mix from INA1 + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x2C, (1U << 1)); // Right speaker mix from INA1 + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3D, 0x17, 0b11111); // Left speaker volume + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x3E, 0x17, 0b11111); // Right speaker volume + success &= i2c_set_reg_mask(I2C5, CODEC_I2C_ADDR, 0x37, 0b101, 0b111); // INA gain + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7)); // Enable INA + success &= i2c_set_reg_bits(I2C5, CODEC_I2C_ADDR, 0x51, (1U << 7)); // Disable global shutdown + if (!success) { + print("Siren codec enable failed\n"); + fault_occurred(FAULT_SIREN_MALFUNCTION); + } + } else { + // Disable INA input. Make sure to retry a few times if the I2C bus is busy. + for (uint8_t i=0U; i<10U; i++) { + if (i2c_clear_reg_bits(I2C5, CODEC_I2C_ADDR, 0x4C, (1U << 7))) { + break; + } + } + } +} + + +void fake_siren_set(bool enabled) { + if (enabled != fake_siren_enabled) { + fake_siren_codec_enable(enabled); + } + + if (enabled) { + register_set_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } else { + register_clear_bits(&DMA1_Stream1->CR, DMA_SxCR_EN); + } + fake_siren_enabled = enabled; +} + +void fake_siren_init(void) { + // Init DAC + register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set_bits(&DAC1->CR, DAC_CR_EN1); + + // Setup DMAMUX (DAC_CH1_DMA as input) + register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); + + // Setup DMA + register_set(&DMA1_Stream1->M0AR, (uint32_t) fake_siren_lut, 0xFFFFFFFFU); + register_set(&DMA1_Stream1->PAR, (uint32_t) &(DAC1->DHR8R1), 0xFFFFFFFFU); + DMA1_Stream1->NDTR = sizeof(fake_siren_lut); + register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); + DMA1_Stream1->CR = (0b11 << DMA_SxCR_PL_Pos); + DMA1_Stream1->CR |= DMA_SxCR_MINC | DMA_SxCR_CIRC | (1 << DMA_SxCR_DIR_Pos); + + // Init trigger timer (around 2.5kHz) + register_set(&TIM7->PSC, 0U, 0xFFFFU); + register_set(&TIM7->ARR, 133U, 0xFFFFU); + register_set(&TIM7->CR2, (0b10 << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); + TIM7->SR = 0U; + TIM7->CR1 |= TIM_CR1_CEN; + + // Enable the I2C to the codec + i2c_init(I2C5); + fake_siren_codec_enable(false); +} diff --git a/panda/board/drivers/fan.h b/panda/board/drivers/fan.h new file mode 100644 index 0000000..1529607 --- /dev/null +++ b/panda/board/drivers/fan.h @@ -0,0 +1,95 @@ +struct fan_state_t { + uint16_t tach_counter; + uint16_t rpm; + uint16_t target_rpm; + uint8_t power; + float error_integral; + uint8_t stall_counter; + uint8_t stall_threshold; + uint8_t total_stall_count; + uint8_t cooldown_counter; +} fan_state_t; +struct fan_state_t fan_state; + +const float FAN_I = 0.001f; + +const uint8_t FAN_TICK_FREQ = 8U; +const uint8_t FAN_STALL_THRESHOLD_MIN = 3U; +const uint8_t FAN_STALL_THRESHOLD_MAX = 8U; + + +void fan_set_power(uint8_t percentage) { + fan_state.target_rpm = ((current_board->fan_max_rpm * CLAMP(percentage, 0U, 100U)) / 100U); +} + +void llfan_init(void); +void fan_init(void) { + fan_state.stall_threshold = FAN_STALL_THRESHOLD_MIN; + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + llfan_init(); +} + +// Call this at FAN_TICK_FREQ +void fan_tick(void) { + if (current_board->fan_max_rpm > 0U) { + // Measure fan RPM + uint16_t fan_rpm_fast = fan_state.tach_counter * (60U * FAN_TICK_FREQ / 4U); // 4 interrupts per rotation + fan_state.tach_counter = 0U; + fan_state.rpm = (fan_rpm_fast + (3U * fan_state.rpm)) / 4U; + + // Stall detection + bool fan_stalled = false; + if (current_board->fan_stall_recovery) { + if (fan_state.target_rpm > 0U) { + if (fan_rpm_fast == 0U) { + fan_state.stall_counter = MIN(fan_state.stall_counter + 1U, 255U); + } else { + fan_state.stall_counter = 0U; + } + + if (fan_state.stall_counter > (fan_state.stall_threshold*FAN_TICK_FREQ)) { + fan_stalled = true; + fan_state.stall_counter = 0U; + fan_state.stall_threshold = CLAMP(fan_state.stall_threshold + 2U, FAN_STALL_THRESHOLD_MIN, FAN_STALL_THRESHOLD_MAX); + fan_state.total_stall_count += 1U; + + // datasheet gives this range as the minimum startup duty + fan_state.error_integral = CLAMP(fan_state.error_integral, 20.0f, 45.0f); + } + } else { + fan_state.stall_counter = 0U; + fan_state.stall_threshold = FAN_STALL_THRESHOLD_MIN; + } + } + + #ifdef DEBUG_FAN + puth(fan_state.target_rpm); + print(" "); puth(fan_rpm_fast); + print(" "); puth(fan_state.power); + print(" "); puth(fan_state.stall_counter); + print("\n"); + #endif + + // Cooldown counter + if (fan_state.target_rpm > 0U) { + fan_state.cooldown_counter = current_board->fan_enable_cooldown_time * FAN_TICK_FREQ; + } else { + if (fan_state.cooldown_counter > 0U) { + fan_state.cooldown_counter--; + } + } + + // Update controller + if (fan_state.target_rpm == 0U) { + fan_state.error_integral = 0.0f; + } else { + float error = fan_state.target_rpm - fan_rpm_fast; + fan_state.error_integral += FAN_I * error; + } + fan_state.power = CLAMP(fan_state.error_integral, 0U, 100U); + + // Set PWM and enable line + pwm_set(TIM3, 3, fan_state.power); + current_board->set_fan_enabled(!fan_stalled && ((fan_state.target_rpm > 0U) || (fan_state.cooldown_counter > 0U))); + } +} diff --git a/panda/board/drivers/fdcan.h b/panda/board/drivers/fdcan.h new file mode 100644 index 0000000..9e2e0df --- /dev/null +++ b/panda/board/drivers/fdcan.h @@ -0,0 +1,267 @@ +// IRQs: FDCAN1_IT0, FDCAN1_IT1 +// FDCAN2_IT0, FDCAN2_IT1 +// FDCAN3_IT0, FDCAN3_IT1 + +#define CANFD + +typedef struct { + volatile uint32_t header[2]; + volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U]; +} canfd_fifo; + +FDCAN_GlobalTypeDef *cans[] = {FDCAN1, FDCAN2, FDCAN3}; + +uint8_t can_irq_number[3][2] = { + { FDCAN1_IT0_IRQn, FDCAN1_IT1_IRQn }, + { FDCAN2_IT0_IRQn, FDCAN2_IT1_IRQn }, + { FDCAN3_IT0_IRQn, FDCAN3_IT1_IRQn }, +}; + +#define CAN_ACK_ERROR 3U + +bool can_set_speed(uint8_t can_number) { + bool ret = true; + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + ret &= llcan_set_speed( + FDCANx, + bus_config[bus_number].can_speed, + bus_config[bus_number].can_data_speed, + bus_config[bus_number].canfd_non_iso, + can_loopback, + (unsigned int)(can_silent) & (1U << can_number) + ); + return ret; +} + +void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint32_t psr_reg = FDCANx->PSR; + uint32_t ecr_reg = FDCANx->ECR; + + can_health[can_number].bus_off = ((psr_reg & FDCAN_PSR_BO) >> FDCAN_PSR_BO_Pos); + can_health[can_number].bus_off_cnt += can_health[can_number].bus_off; + can_health[can_number].error_warning = ((psr_reg & FDCAN_PSR_EW) >> FDCAN_PSR_EW_Pos); + can_health[can_number].error_passive = ((psr_reg & FDCAN_PSR_EP) >> FDCAN_PSR_EP_Pos); + + can_health[can_number].last_error = ((psr_reg & FDCAN_PSR_LEC) >> FDCAN_PSR_LEC_Pos); + if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) { + can_health[can_number].last_stored_error = can_health[can_number].last_error; + } + + can_health[can_number].last_data_error = ((psr_reg & FDCAN_PSR_DLEC) >> FDCAN_PSR_DLEC_Pos); + if ((can_health[can_number].last_data_error != 0U) && (can_health[can_number].last_data_error != 7U)) { + can_health[can_number].last_data_stored_error = can_health[can_number].last_data_error; + } + + can_health[can_number].receive_error_cnt = ((ecr_reg & FDCAN_ECR_REC) >> FDCAN_ECR_REC_Pos); + can_health[can_number].transmit_error_cnt = ((ecr_reg & FDCAN_ECR_TEC) >> FDCAN_ECR_TEC_Pos); + + can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate; + can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate; + + + if (ir_reg != 0U) { + // Clear error interrupts + FDCANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); + can_health[can_number].total_error_cnt += 1U; + // Check for RX FIFO overflow + if ((ir_reg & (FDCAN_IR_RF0L)) != 0) { + can_health[can_number].total_rx_lost_cnt += 1U; + } + // Cases: + // 1. while multiplexing between buses 1 and 3 we are getting ACK errors that overwhelm CAN core, by resetting it recovers faster + // 2. H7 gets stuck in bus off recovery state indefinitely + if ((((can_health[can_number].last_error == CAN_ACK_ERROR) || (can_health[can_number].last_data_error == CAN_ACK_ERROR)) && (can_health[can_number].transmit_error_cnt > 127U)) || + ((ir_reg & FDCAN_IR_BO) != 0)) { + can_health[can_number].can_core_reset_cnt += 1U; + can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (FDCANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset + llcan_clear_send(FDCANx); + } + } +} + +// ***************************** CAN ***************************** +// FDFDCANx_IT1 IRQ Handler (TX) +void process_can(uint8_t can_number) { + if (can_number != 0xffU) { + ENTER_CRITICAL(); + + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag + + if ((FDCANx->TXFQS & FDCAN_TXFQS_TFQF) == 0) { + CANPacket_t to_send; + if (can_pop(can_queues[bus_number], &to_send)) { + if (can_check_checksum(&to_send)) { + can_health[can_number].total_tx_cnt += 1U; + + uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); + // get the index of the next TX FIFO element (0 to FDCAN_TX_FIFO_EL_CNT - 1) + uint32_t tx_index = (FDCANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1F; + // only send if we have received a packet + canfd_fifo *fifo; + fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); + + fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); + uint32_t canfd_enabled_header = bus_config[can_number].canfd_enabled ? (1UL << 21) : 0UL; + uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; + fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; + + uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_send.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); + } + + FDCANx->TXBAR = (1UL << tx_index); + + // Send back to USB + CANPacket_t to_push; + + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = to_send.extended; + to_push.addr = to_send.addr; + to_push.bus = bus_number; + to_push.data_len_code = to_send.data_len_code; + (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_push); + + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + } else { + can_health[can_number].total_tx_checksum_error_cnt += 1U; + } + + refresh_can_tx_slots_available(); + } + } + EXIT_CRITICAL(); + } +} + +// FDFDCANx_IT0 IRQ Handler (RX and errors) +// blink blue when we are receiving CAN messages +void can_rx(uint8_t can_number) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); + + uint32_t ir_reg = FDCANx->IR; + + // Clear all new messages from Rx FIFO 0 + FDCANx->IR |= FDCAN_IR_RF0N; + while((FDCANx->RXF0S & FDCAN_RXF0S_F0FL) != 0) { + can_health[can_number].total_rx_cnt += 1U; + + // can is live + pending_can_live = 1; + + // get the index of the next RX FIFO element (0 to FDCAN_RX_FIFO_0_EL_CNT - 1) + uint32_t rx_fifo_idx = (uint8_t)((FDCANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3F); + + // Recommended to offset get index by at least +1 if RX FIFO is in overwrite mode and full (datasheet) + if((FDCANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { + rx_fifo_idx = ((rx_fifo_idx + 1U) >= FDCAN_RX_FIFO_0_EL_CNT) ? 0U : (rx_fifo_idx + 1U); + can_health[can_number].total_rx_lost_cnt += 1U; // At least one message was lost + } + + uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); + CANPacket_t to_push; + canfd_fifo *fifo; + + // getting address + fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); + + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (fifo->header[0] >> 30) & 0x1U; + to_push.addr = ((to_push.extended != 0U) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); + to_push.bus = bus_number; + to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); + + bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); + bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); + + uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + WORD_TO_BYTE_ARRAY(&to_push.data[i*4U], fifo->data_word[i]); + } + can_set_checksum(&to_push); + + // forwarding (panda only) + int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr); + if (bus_fwd_num < 0) { + bus_fwd_num = bus_config[can_number].forwarding_bus; + } + if (bus_fwd_num != -1) { + CANPacket_t to_send; + + to_send.returned = 0U; + to_send.rejected = 0U; + to_send.extended = to_push.extended; + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); + can_set_checksum(&to_send); + + can_send(&to_send, bus_fwd_num, true); + can_health[can_number].total_fwd_cnt += 1U; + } + + safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U; + ignition_can_hook(&to_push); + + current_board->set_led(LED_BLUE, true); + rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; + + // Enable CAN FD and BRS if CAN FD message was received + if (!(bus_config[can_number].canfd_enabled) && (canfd_frame)) { + bus_config[can_number].canfd_enabled = true; + } + if (!(bus_config[can_number].brs_enabled) && (brs_frame)) { + bus_config[can_number].brs_enabled = true; + } + + // update read index + FDCANx->RXF0A = rx_fifo_idx; + } + + // Error handling + if ((ir_reg & (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L)) != 0) { + update_can_health_pkt(can_number, ir_reg); + } +} + +void FDCAN1_IT0_IRQ_Handler(void) { can_rx(0); } +void FDCAN1_IT1_IRQ_Handler(void) { process_can(0); } + +void FDCAN2_IT0_IRQ_Handler(void) { can_rx(1); } +void FDCAN2_IT1_IRQ_Handler(void) { process_can(1); } + +void FDCAN3_IT0_IRQ_Handler(void) { can_rx(2); } +void FDCAN3_IT1_IRQ_Handler(void) { process_can(2); } + +bool can_init(uint8_t can_number) { + bool ret = false; + + REGISTER_INTERRUPT(FDCAN1_IT0_IRQn, FDCAN1_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN1_IT1_IRQn, FDCAN1_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1) + REGISTER_INTERRUPT(FDCAN2_IT0_IRQn, FDCAN2_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN2_IT1_IRQn, FDCAN2_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2) + REGISTER_INTERRUPT(FDCAN3_IT0_IRQn, FDCAN3_IT0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) + + if (can_number != 0xffU) { + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); + ret &= can_set_speed(can_number); + ret &= llcan_init(FDCANx); + // in case there are queued up messages + process_can(can_number); + } + return ret; +} diff --git a/panda/board/drivers/gmlan_alt.h b/panda/board/drivers/gmlan_alt.h new file mode 100644 index 0000000..2dbc381 --- /dev/null +++ b/panda/board/drivers/gmlan_alt.h @@ -0,0 +1,270 @@ +#define GMLAN_TICKS_PER_SECOND 33300 //1sec @ 33.3kbps +#define GMLAN_TICKS_PER_TIMEOUT_TICKLE 500 //15ms @ 33.3kbps +#define GMLAN_HIGH 0 //0 is high on bus (dominant) +#define GMLAN_LOW 1 //1 is low on bus + +#define DISABLED -1 +#define BITBANG 0 +#define GPIO_SWITCH 1 + +#define MAX_BITS_CAN_PACKET (200) + +int gmlan_alt_mode = DISABLED; + +// returns out_len +int do_bitstuff(char *out, const char *in, int in_len) { + int last_bit = -1; + int bit_cnt = 0; + int j = 0; + for (int i = 0; i < in_len; i++) { + char bit = in[i]; + out[j] = bit; + j++; + + // do the stuffing + if (bit == (char)last_bit) { + bit_cnt++; + if (bit_cnt == 5) { + // 5 in a row the same, do stuff + last_bit = !bit ? 1 : 0; + out[j] = last_bit; + j++; + bit_cnt = 1; + } + } else { + // this is a new bit + last_bit = (int)bit; + bit_cnt = 1; + } + } + return j; +} + +int append_crc(char *in, int in_len) { + unsigned int crc = 0; + for (int i = 0; i < in_len; i++) { + crc <<= 1; + if (((unsigned int)(in[i]) ^ ((crc >> 15) & 1U)) != 0U) { + crc = crc ^ 0x4599U; + } + crc &= 0x7fffU; + } + int in_len_copy = in_len; + for (int i = 14; i >= 0; i--) { + in[in_len_copy] = (crc >> (unsigned int)(i)) & 1U; + in_len_copy++; + } + return in_len_copy; +} + +int append_bits(char *in, int in_len, const char *app, int app_len) { + int in_len_copy = in_len; + for (int i = 0; i < app_len; i++) { + in[in_len_copy] = app[i]; + in_len_copy++; + } + return in_len_copy; +} + +int append_int(char *in, int in_len, int val, int val_len) { + int in_len_copy = in_len; + for (int i = val_len - 1; i >= 0; i--) { + in[in_len_copy] = ((unsigned int)(val) & (1U << (unsigned int)(i))) != 0U; + in_len_copy++; + } + return in_len_copy; +} + +int get_bit_message(char *out, const CANPacket_t *to_bang) { + char pkt[MAX_BITS_CAN_PACKET]; + char footer[] = { + 1, // CRC delimiter + 1, // ACK + 1, // ACK delimiter + 1,1,1,1,1,1,1, // EOF + 1,1,1, // IFS + }; + + int len = 0; + + // test packet + int dlc_len = GET_LEN(to_bang); + len = append_int(pkt, len, 0, 1); // Start-of-frame + + if (to_bang->extended != 0U) { + // extended identifier + len = append_int(pkt, len, GET_ADDR(to_bang) >> 18, 11); // Identifier + len = append_int(pkt, len, 3, 2); // SRR+IDE + len = append_int(pkt, len, (GET_ADDR(to_bang)) & ((1UL << 18) - 1U), 18); // Identifier + len = append_int(pkt, len, 0, 3); // RTR+r1+r0 + } else { + // standard identifier + len = append_int(pkt, len, GET_ADDR(to_bang), 11); // Identifier + len = append_int(pkt, len, 0, 3); // RTR+IDE+reserved + } + + len = append_int(pkt, len, dlc_len, 4); // Data length code + + // append data + for (int i = 0; i < dlc_len; i++) { + len = append_int(pkt, len, to_bang->data[i], 8); + } + + // append crc + len = append_crc(pkt, len); + + // do bitstuffing + len = do_bitstuff(out, pkt, len); + + // append footer + len = append_bits(out, len, footer, sizeof(footer)); + return len; +} + +void TIM12_IRQ_Handler(void); + +void setup_timer(void) { + // register interrupt + REGISTER_INTERRUPT(TIM8_BRK_TIM12_IRQn, TIM12_IRQ_Handler, 40000U, FAULT_INTERRUPT_RATE_GMLAN) + + // setup + register_set(&(TIM12->PSC), (APB1_TIMER_FREQ-1U), 0xFFFFU); // Tick on 1 us + register_set(&(TIM12->CR1), TIM_CR1_CEN, 0x3FU); // Enable + register_set(&(TIM12->ARR), (30U-1U), 0xFFFFU); // 33.3 kbps + + // in case it's disabled + NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn); + + // run the interrupt + register_set(&(TIM12->DIER), TIM_DIER_UIE, 0x5F5FU); // Update interrupt + TIM12->SR = 0; +} + +int gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; //GMLAN transceiver times out every 17ms held high; tickle every 15ms +int can_timeout_counter = GMLAN_TICKS_PER_SECOND; //1 second + +int inverted_bit_to_send = GMLAN_HIGH; +int gmlan_switch_below_timeout = -1; +int gmlan_switch_timeout_enable = 0; + +void set_bitbanged_gmlan(int val) { + if (val != 0) { + register_set_bits(&(GPIOB->ODR), (1UL << 13)); + } else { + register_clear_bits(&(GPIOB->ODR), (1UL << 13)); + } +} + +char pkt_stuffed[MAX_BITS_CAN_PACKET]; +int gmlan_sending = -1; +int gmlan_sendmax = -1; +bool gmlan_send_ok = true; + +int gmlan_silent_count = 0; +int gmlan_fail_count = 0; +#define REQUIRED_SILENT_TIME 10 +#define MAX_FAIL_COUNT 10 + +void TIM12_IRQ_Handler(void) { + if (gmlan_alt_mode == BITBANG) { + if ((TIM12->SR & TIM_SR_UIF) && (gmlan_sendmax != -1)) { + int read = get_gpio_input(GPIOB, 12); + if (gmlan_silent_count < REQUIRED_SILENT_TIME) { + if (read == 0) { + gmlan_silent_count = 0; + } else { + gmlan_silent_count++; + } + } else { + bool retry = 0; + // in send loop + if ((gmlan_sending > 0) && // not first bit + ((read == 0) && (pkt_stuffed[gmlan_sending-1] == (char)1)) && // bus wrongly dominant + (gmlan_sending != (gmlan_sendmax - 11))) { //not ack bit + print("GMLAN ERR: bus driven at "); + puth(gmlan_sending); + print("\n"); + retry = 1; + } else if ((read == 1) && (gmlan_sending == (gmlan_sendmax - 11))) { // recessive during ACK + print("GMLAN ERR: didn't recv ACK\n"); + retry = 1; + } else { + // do not retry + } + if (retry) { + // reset sender (retry after 7 silent) + set_bitbanged_gmlan(1); // recessive + gmlan_silent_count = 0; + gmlan_sending = 0; + gmlan_fail_count++; + if (gmlan_fail_count == MAX_FAIL_COUNT) { + print("GMLAN ERR: giving up send\n"); + gmlan_send_ok = false; + } + } else { + set_bitbanged_gmlan(pkt_stuffed[gmlan_sending]); + gmlan_sending++; + } + } + if ((gmlan_sending == gmlan_sendmax) || (gmlan_fail_count == MAX_FAIL_COUNT)) { + set_bitbanged_gmlan(1); // recessive + set_gpio_mode(GPIOB, 13, MODE_INPUT); + register_clear_bits(&(TIM12->DIER), TIM_DIER_UIE); // No update interrupt + register_set(&(TIM12->CR1), 0U, 0x3FU); // Disable timer + gmlan_sendmax = -1; // exit + } + } + } else if (gmlan_alt_mode == GPIO_SWITCH) { + if ((TIM12->SR & TIM_SR_UIF) && (gmlan_switch_below_timeout != -1)) { + if ((can_timeout_counter == 0) && gmlan_switch_timeout_enable) { + //it has been more than 1 second since timeout was reset; disable timer and restore the GMLAN output + set_gpio_output(GPIOB, 13, GMLAN_LOW); + gmlan_switch_below_timeout = -1; + gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; + gmlan_alt_mode = DISABLED; + } + else { + can_timeout_counter--; + if (gmlan_timeout_counter == 0) { + //Send a 1 (bus low) every 15ms to reset the GMLAN transceivers timeout + gmlan_timeout_counter = GMLAN_TICKS_PER_TIMEOUT_TICKLE; + set_gpio_output(GPIOB, 13, GMLAN_LOW); + } + else { + set_gpio_output(GPIOB, 13, inverted_bit_to_send); + gmlan_timeout_counter--; + } + } + } + } else { + // Invalid GMLAN mode. Do not put a print statement here, way too fast to keep up with + } + TIM12->SR = 0; +} + +bool bitbang_gmlan(const CANPacket_t *to_bang) { + gmlan_send_ok = true; + gmlan_alt_mode = BITBANG; + +#ifdef HW_TYPE_DOS + if (hw_type == HW_TYPE_DOS) { + if (gmlan_sendmax == -1) { + int len = get_bit_message(pkt_stuffed, to_bang); + gmlan_fail_count = 0; + gmlan_silent_count = 0; + gmlan_sending = 0; + gmlan_sendmax = len; + // setup for bitbang loop + set_bitbanged_gmlan(1); // recessive + set_gpio_mode(GPIOB, 13, MODE_OUTPUT); + + // 33kbps + setup_timer(); + } + } +#else + UNUSED(to_bang); +#endif + + return gmlan_send_ok; +} diff --git a/panda/board/drivers/gpio.h b/panda/board/drivers/gpio.h new file mode 100644 index 0000000..bf96a03 --- /dev/null +++ b/panda/board/drivers/gpio.h @@ -0,0 +1,92 @@ +#define MODE_INPUT 0 +#define MODE_OUTPUT 1 +#define MODE_ALTERNATE 2 +#define MODE_ANALOG 3 + +#define PULL_NONE 0 +#define PULL_UP 1 +#define PULL_DOWN 2 + +#define OUTPUT_TYPE_PUSH_PULL 0U +#define OUTPUT_TYPE_OPEN_DRAIN 1U + +typedef struct { + GPIO_TypeDef *bank; + uint8_t pin; +} gpio_t; + +void set_gpio_mode(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->MODER; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->MODER), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +void set_gpio_output(GPIO_TypeDef *GPIO, unsigned int pin, bool enabled) { + ENTER_CRITICAL(); + if (enabled) { + register_set_bits(&(GPIO->ODR), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->ODR), (1UL << pin)); + } + set_gpio_mode(GPIO, pin, MODE_OUTPUT); + EXIT_CRITICAL(); +} + +void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int output_type){ + ENTER_CRITICAL(); + if(output_type == OUTPUT_TYPE_OPEN_DRAIN) { + register_set_bits(&(GPIO->OTYPER), (1UL << pin)); + } else { + register_clear_bits(&(GPIO->OTYPER), (1U << pin)); + } + EXIT_CRITICAL(); +} + +void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->AFR[pin >> 3U]; + tmp &= ~(0xFU << ((pin & 7U) * 4U)); + tmp |= mode << ((pin & 7U) * 4U); + register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU); + set_gpio_mode(GPIO, pin, MODE_ALTERNATE); + EXIT_CRITICAL(); +} + +void set_gpio_pullup(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) { + ENTER_CRITICAL(); + uint32_t tmp = GPIO->PUPDR; + tmp &= ~(3U << (pin * 2U)); + tmp |= (mode << (pin * 2U)); + register_set(&(GPIO->PUPDR), tmp, 0xFFFFFFFFU); + EXIT_CRITICAL(); +} + +int get_gpio_input(const GPIO_TypeDef *GPIO, unsigned int pin) { + return (GPIO->IDR & (1UL << pin)) == (1UL << pin); +} + +void gpio_set_all_output(const gpio_t *pins, uint8_t num_pins, bool enabled) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, enabled); + } +} + +void gpio_set_bitmask(const gpio_t *pins, uint8_t num_pins, uint32_t bitmask) { + for (uint8_t i = 0; i < num_pins; i++) { + set_gpio_output(pins[i].bank, pins[i].pin, (bitmask >> i) & 1U); + } +} + +// Detection with internal pullup +#define PULL_EFFECTIVE_DELAY 4096 +bool detect_with_pull(GPIO_TypeDef *GPIO, int pin, int mode) { + set_gpio_mode(GPIO, pin, MODE_INPUT); + set_gpio_pullup(GPIO, pin, mode); + for (volatile int i=0; iharness_config->has_harness) { + bool drive_relay = intercept; + if (harness.status == HARNESS_STATUS_NC) { + // no harness, no relay to drive + drive_relay = false; + } + + if (drive_relay || ignition_relay) { + harness.relay_driven = true; + } + + // wait until we're not reading the analog voltages anymore + while (harness.sbu_adc_lock) {} + + if (harness.status == HARNESS_STATUS_NORMAL) { + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !ignition_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay); + } else { + set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); + } + + if (!(drive_relay || ignition_relay)) { + harness.relay_driven = false; + } + } +} + +bool harness_check_ignition(void) { + bool ret = false; + + // wait until we're not reading the analog voltages anymore + while (harness.sbu_adc_lock) {} + + switch(harness.status){ + case HARNESS_STATUS_NORMAL: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1); + break; + case HARNESS_STATUS_FLIPPED: + ret = !get_gpio_input(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2); + break; + default: + break; + } + return ret; +} + +uint8_t harness_detect_orientation(void) { + uint8_t ret = harness.status; + + #ifndef BOOTSTUB + // We can't detect orientation if the relay is being driven + if (!harness.relay_driven && current_board->harness_config->has_harness) { + harness.sbu_adc_lock = true; + set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG); + set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG); + + harness.sbu1_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU1); + harness.sbu2_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU2); + uint16_t detection_threshold = current_board->avdd_mV / 2U; + + // Detect connection and orientation + if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){ + if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) { + // orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign)) + ret = HARNESS_STATUS_FLIPPED; + } else { + // orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign)) + // (SBU1->SBU2 is the normal orientation connection per USB-C cable spec) + ret = HARNESS_STATUS_NORMAL; + } + } else { + ret = HARNESS_STATUS_NC; + } + + // Pins are not 5V tolerant in ADC mode + set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT); + set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT); + harness.sbu_adc_lock = false; + } + #endif + + return ret; +} + +void harness_tick(void) { + harness.status = harness_detect_orientation(); +} + +void harness_init(void) { + // delay such that the connection is fully made before trying orientation detection + current_board->set_led(LED_BLUE, true); + delay(10000000); + current_board->set_led(LED_BLUE, false); + + // try to detect orientation + harness.status = harness_detect_orientation(); + if (harness.status != HARNESS_STATUS_NC) { + print("detected car harness with orientation "); puth2(harness.status); print("\n"); + } else { + print("failed to detect car harness!\n"); + } + + // keep buses connected by default + set_intercept_relay(false, false); +} diff --git a/panda/board/drivers/interrupts.h b/panda/board/drivers/interrupts.h new file mode 100644 index 0000000..d4c72be --- /dev/null +++ b/panda/board/drivers/interrupts.h @@ -0,0 +1,99 @@ +typedef struct interrupt { + IRQn_Type irq_type; + void (*handler)(void); + uint32_t call_counter; + uint32_t call_rate; + uint32_t max_call_rate; // Call rate is defined as the amount of calls each second + uint32_t call_rate_fault; +} interrupt; + +void interrupt_timer_init(void); +uint32_t microsecond_timer_get(void); + +void unused_interrupt_handler(void) { + // Something is wrong if this handler is called! + print("Unused interrupt handler called!\n"); + fault_occurred(FAULT_UNUSED_INTERRUPT_HANDLED); +} + +interrupt interrupts[NUM_INTERRUPTS]; + +#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \ + interrupts[irq_num].irq_type = (irq_num); \ + interrupts[irq_num].handler = (func_ptr); \ + interrupts[irq_num].call_counter = 0U; \ + interrupts[irq_num].call_rate = 0U; \ + interrupts[irq_num].max_call_rate = (call_rate_max); \ + interrupts[irq_num].call_rate_fault = (rate_fault); + +bool check_interrupt_rate = false; + +uint8_t interrupt_depth = 0U; +uint32_t last_time = 0U; +uint32_t idle_time = 0U; +uint32_t busy_time = 0U; +float interrupt_load = 0.0f; + +void handle_interrupt(IRQn_Type irq_type){ + ENTER_CRITICAL(); + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + idle_time += get_ts_elapsed(time, last_time); + last_time = time; + } + interrupt_depth += 1U; + EXIT_CRITICAL(); + + 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)) { + fault_occurred(interrupts[irq_type].call_rate_fault); + } + + ENTER_CRITICAL(); + interrupt_depth -= 1U; + if (interrupt_depth == 0U) { + uint32_t time = microsecond_timer_get(); + busy_time += get_ts_elapsed(time, last_time); + last_time = time; + } + EXIT_CRITICAL(); +} + +// Every second +void interrupt_timer_handler(void) { + if (INTERRUPT_TIMER->SR != 0) { + for (uint16_t i = 0U; i < NUM_INTERRUPTS; i++) { + // Log IRQ call rate faults + if (check_interrupt_rate && (interrupts[i].call_counter > interrupts[i].max_call_rate)) { + print("Interrupt 0x"); puth(i); print(" fired too often (0x"); puth(interrupts[i].call_counter); print("/s)!\n"); + } + + // Reset interrupt counters + interrupts[i].call_rate = interrupts[i].call_counter; + interrupts[i].call_counter = 0U; + } + + // Calculate interrupt load + // The bootstub does not have the FPU enabled, so can't do float operations. +#if !defined(BOOTSTUB) + interrupt_load = ((busy_time + idle_time) > 0U) ? ((float) (((float) busy_time) / (busy_time + idle_time))) : 0.0f; +#endif + idle_time = 0U; + busy_time = 0U; + } + INTERRUPT_TIMER->SR = 0; +} + +void init_interrupts(bool check_rate_limit){ + check_interrupt_rate = check_rate_limit; + + for(uint16_t i=0U; iCR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); + + // Set channel as PWM mode 1 and enable output + switch(channel){ + case 1U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC1E); + break; + case 2U: + register_set_bits(&(TIM->CCMR1), (TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC2E); + break; + case 3U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC3E); + break; + case 4U: + register_set_bits(&(TIM->CCMR2), (TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE)); + register_set_bits(&(TIM->CCER), TIM_CCER_CC4E); + break; + default: + break; + } + + // Set max counter value + register_set(&(TIM->ARR), PWM_COUNTER_OVERFLOW, 0xFFFFU); + + // Update registers and clear counter + TIM->EGR |= TIM_EGR_UG; +} + +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage){ + uint16_t comp_value = (((uint16_t) percentage * PWM_COUNTER_OVERFLOW) / 100U); + switch(channel){ + case 1U: + register_set(&(TIM->CCR1), comp_value, 0xFFFFU); + break; + case 2U: + register_set(&(TIM->CCR2), comp_value, 0xFFFFU); + break; + case 3U: + register_set(&(TIM->CCR3), comp_value, 0xFFFFU); + break; + case 4U: + register_set(&(TIM->CCR4), comp_value, 0xFFFFU); + break; + default: + break; + } +} diff --git a/panda/board/drivers/registers.h b/panda/board/drivers/registers.h new file mode 100644 index 0000000..a5f8b02 --- /dev/null +++ b/panda/board/drivers/registers.h @@ -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 + print("Hash collision: address 0x"); puth((uint32_t) addr); print("!\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 wd_state.threshold) { + print("WD timeout 0x"); puth(et); print("\n"); + fault_occurred(wd_state.fault); + } + + wd_state.last_ts = ts; +} + +void simple_watchdog_init(uint32_t fault, uint32_t threshold) { + wd_state.fault = fault; + wd_state.threshold = threshold; + wd_state.last_ts = microsecond_timer_get(); +} diff --git a/panda/board/drivers/spi.h b/panda/board/drivers/spi.h new file mode 100644 index 0000000..1a7b9be --- /dev/null +++ b/panda/board/drivers/spi.h @@ -0,0 +1,258 @@ +#pragma once + +#include "crc.h" + +#define SPI_TIMEOUT_US 10000U + +// got max rate from hitting a non-existent endpoint +// in a tight loop, plus some buffer +#define SPI_IRQ_RATE 16000U + +#ifdef STM32H7 +#define SPI_BUF_SIZE 2048U +// H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 +__attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; +__attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; +#else +#define SPI_BUF_SIZE 1024U +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 +#define SPI_HACK 0x79U +#define SPI_DACK 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 +}; + +bool spi_tx_dma_done = false; +uint8_t spi_state = SPI_STATE_HEADER; +uint8_t spi_endpoint; +uint16_t spi_data_len_mosi; +uint16_t spi_data_len_miso; +uint16_t spi_checksum_error_count = 0; +bool spi_can_tx_ready = false; + +const char version_text[] = "VERSION"; + +#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 can_tx_comms_resume_spi(void) { + spi_can_tx_ready = true; +} + +uint16_t spi_version_packet(uint8_t *out) { + // this protocol version request is a stable portion of + // the panda's SPI protocol. its contents match that of the + // panda USB descriptors and are sufficent to list/enumerate + // a panda, determine panda type, and bootstub status. + + // the response is: + // VERSION + 2 byte data length + data + CRC8 + + // echo "VERSION" + (void)memcpy(out, version_text, 7); + + // write response + uint16_t data_len = 0; + uint16_t data_pos = 7U + 2U; + + // write serial + #ifdef UID_BASE + (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); + data_len += 12U; + #endif + + // HW type + out[data_pos + data_len] = hw_type; + data_len += 1U; + + // bootstub + out[data_pos + data_len] = USB_PID & 0xFFU; + data_len += 1U; + + // SPI protocol version + out[data_pos + data_len] = 0x2; + data_len += 1U; + + // data length + out[7] = data_len & 0xFFU; + out[8] = (data_len >> 8) & 0xFFU; + + // CRC8 + uint16_t resp_len = data_pos + data_len; + out[resp_len] = crc_checksum(out, resp_len, 0xD5U); + resp_len += 1U; + + return resp_len; +} + +void spi_init(void) { + // platform init + llspi_init(); + + // Start the first packet! + spi_state = SPI_STATE_HEADER; + llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE); +} + +bool validate_checksum(const 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; + for(uint16_t i = 0U; i < len; i++){ + checksum ^= data[i]; + } + return checksum == 0U; +} + +void spi_rx_done(void) { + uint16_t response_len = 0U; + uint8_t next_rx_state = SPI_STATE_HEADER_NACK; + bool checksum_valid = false; + + // 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 + print("- 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; + response_len = 1U; + } + } 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; + (void)memcpy(&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); + response_ack = true; + } else { + response_ack = false; + print("SPI: CAN NACK\n"); + } + } else { + print("SPI: did expect data for can_write\n"); + } + } else { + print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); + } + } else { + // Checksum was incorrect + response_ack = false; + 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"); + } + + 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[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; + 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; + } + llspi_miso_dma(spi_buf_tx, response_len); + + spi_state = next_rx_state; + if (!checksum_valid && (spi_checksum_error_count < __UINT16_MAX__)) { + spi_checksum_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"); + } +} diff --git a/panda/board/drivers/timers.h b/panda/board/drivers/timers.h new file mode 100644 index 0000000..d7f3f01 --- /dev/null +++ b/panda/board/drivers/timers.h @@ -0,0 +1,31 @@ +void timer_init(TIM_TypeDef *TIM, int psc) { + register_set(&(TIM->PSC), (psc-1), 0xFFFFU); + register_set(&(TIM->DIER), TIM_DIER_UIE, 0x5F5FU); + register_set(&(TIM->CR1), TIM_CR1_CEN, 0x3FU); + TIM->SR = 0; +} + +void microsecond_timer_init(void) { + MICROSECOND_TIMER->PSC = (APB1_TIMER_FREQ - 1U); + MICROSECOND_TIMER->CR1 = TIM_CR1_CEN; + MICROSECOND_TIMER->EGR = TIM_EGR_UG; +} + +uint32_t microsecond_timer_get(void) { + return MICROSECOND_TIMER->CNT; +} + +void interrupt_timer_init(void) { + enable_interrupt_timer(); + REGISTER_INTERRUPT(INTERRUPT_TIMER_IRQ, interrupt_timer_handler, 1, FAULT_INTERRUPT_RATE_INTERRUPTS) + register_set(&(INTERRUPT_TIMER->PSC), ((uint16_t)(15.25*APB1_TIMER_FREQ)-1U), 0xFFFFU); + 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); +} + +void tick_timer_init(void) { + timer_init(TICK_TIMER, (uint16_t)((15.25*APB2_TIMER_FREQ)/8U)); + NVIC_EnableIRQ(TICK_TIMER_IRQ); +} diff --git a/panda/board/drivers/uart.h b/panda/board/drivers/uart.h new file mode 100644 index 0000000..01d8c2a --- /dev/null +++ b/panda/board/drivers/uart.h @@ -0,0 +1,196 @@ +// IRQs: USART2, USART3, UART5 + +// ***************************** Definitions ***************************** +#define FIFO_SIZE_INT 0x400U + +typedef struct uart_ring { + volatile uint16_t w_ptr_tx; + volatile uint16_t r_ptr_tx; + uint8_t *elems_tx; + uint32_t tx_fifo_size; + volatile uint16_t w_ptr_rx; + volatile uint16_t r_ptr_rx; + uint8_t *elems_rx; + uint32_t rx_fifo_size; + USART_TypeDef *uart; + void (*callback)(struct uart_ring*); + bool overwrite; +} uart_ring; + +#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, overwrite_mode) \ + uint8_t elems_rx_##x[size_rx]; \ + uint8_t elems_tx_##x[size_tx]; \ + uart_ring uart_ring_##x = { \ + .w_ptr_tx = 0, \ + .r_ptr_tx = 0, \ + .elems_tx = ((uint8_t *)&(elems_tx_##x)), \ + .tx_fifo_size = (size_tx), \ + .w_ptr_rx = 0, \ + .r_ptr_rx = 0, \ + .elems_rx = ((uint8_t *)&(elems_rx_##x)), \ + .rx_fifo_size = (size_rx), \ + .uart = (uart_ptr), \ + .callback = (callback_ptr), \ + .overwrite = (overwrite_mode) \ + }; + +// ***************************** Function prototypes ***************************** +void debug_ring_callback(uart_ring *ring); +void uart_tx_ring(uart_ring *q); +void uart_send_break(uart_ring *u); + +// ******************************** UART buffers ******************************** + +// debug = USART2 +UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, true) + +// SOM debug = UART7 +#ifdef STM32H7 + UART_BUFFER(som_debug, FIFO_SIZE_INT, FIFO_SIZE_INT, UART7, NULL, true) +#else + // UART7 is not available on F4 + UART_BUFFER(som_debug, 1U, 1U, NULL, NULL, true) +#endif + +uart_ring *get_ring_by_number(int a) { + uart_ring *ring = NULL; + switch(a) { + case 0: + ring = &uart_ring_debug; + break; + case 4: + ring = &uart_ring_som_debug; + break; + default: + ring = NULL; + break; + } + return ring; +} + +// ************************* Low-level buffer functions ************************* +bool get_char(uart_ring *q, char *elem) { + bool ret = false; + + ENTER_CRITICAL(); + if (q->w_ptr_rx != q->r_ptr_rx) { + if (elem != NULL) *elem = q->elems_rx[q->r_ptr_rx]; + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool injectc(uart_ring *q, char elem) { + int ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = elem; + q->w_ptr_rx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + return ret; +} + +bool put_char(uart_ring *q, char elem) { + bool ret = false; + uint16_t next_w_ptr; + + ENTER_CRITICAL(); + next_w_ptr = (q->w_ptr_tx + 1U) % q->tx_fifo_size; + + if ((next_w_ptr == q->r_ptr_tx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + if (next_w_ptr != q->r_ptr_tx) { + q->elems_tx[q->w_ptr_tx] = elem; + q->w_ptr_tx = next_w_ptr; + ret = true; + } + EXIT_CRITICAL(); + + uart_tx_ring(q); + + return ret; +} + +void clear_uart_buff(uart_ring *q) { + ENTER_CRITICAL(); + q->w_ptr_tx = 0; + q->r_ptr_tx = 0; + q->w_ptr_rx = 0; + q->r_ptr_rx = 0; + EXIT_CRITICAL(); +} + +// ************************ High-level debug functions ********************** +void putch(const char a) { + // misra-c2012-17.7: serial debug function, ok to ignore output + (void)injectc(&uart_ring_debug, a); +} + +void print(const char *a) { + for (const char *in = a; *in; in++) { + if (*in == '\n') putch('\r'); + putch(*in); + } +} + +void putui(uint32_t i) { + uint32_t i_copy = i; + char str[11]; + uint8_t idx = 10; + str[idx] = '\0'; + idx--; + do { + str[idx] = (i_copy % 10U) + 0x30U; + idx--; + i_copy /= 10; + } while (i_copy != 0U); + print(&str[idx + 1U]); +} + +void puthx(uint32_t i, uint8_t len) { + const char c[] = "0123456789abcdef"; + for (int pos = ((int)len * 4) - 4; pos > -4; pos -= 4) { + putch(c[(i >> (unsigned int)(pos)) & 0xFU]); + } +} + +void puth(unsigned int i) { + puthx(i, 8U); +} + +void puth2(unsigned int i) { + puthx(i, 2U); +} + +void puth4(unsigned int i) { + puthx(i, 4U); +} + +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"); + puth2(((const unsigned char*)a)[i]); + print(" "); + } + } + print("\n"); +} diff --git a/panda/board/drivers/usb.h b/panda/board/drivers/usb.h new file mode 100644 index 0000000..5ecbea2 --- /dev/null +++ b/panda/board/drivers/usb.h @@ -0,0 +1,943 @@ +// IRQs: OTG_FS + +typedef union { + uint16_t w; + struct BW { + uint8_t msb; + uint8_t lsb; + } + bw; +} +uint16_t_uint8_t; + +typedef union _USB_Setup { + uint32_t d8[2]; + struct _SetupPkt_Struc + { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t_uint8_t wValue; + uint16_t_uint8_t wIndex; + uint16_t_uint8_t wLength; + } b; +} +USB_Setup_TypeDef; + +bool usb_enumerated = false; +uint16_t usb_last_frame_num = 0U; + +void usb_init(void); +void refresh_can_tx_slots_available(void); + +// **** supporting defines **** + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +#define USB_DESC_TYPE_DEVICE 0x01 +#define USB_DESC_TYPE_CONFIGURATION 0x02 +#define USB_DESC_TYPE_STRING 0x03 +#define USB_DESC_TYPE_INTERFACE 0x04 +#define USB_DESC_TYPE_ENDPOINT 0x05 +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 0x07 +#define USB_DESC_TYPE_BINARY_OBJECT_STORE 0x0f + +// offsets for configuration strings +#define STRING_OFFSET_LANGID 0x00 +#define STRING_OFFSET_IMANUFACTURER 0x01 +#define STRING_OFFSET_IPRODUCT 0x02 +#define STRING_OFFSET_ISERIAL 0x03 +#define STRING_OFFSET_ICONFIGURATION 0x04 +#define STRING_OFFSET_IINTERFACE 0x05 + +// WebUSB requests +#define WEBUSB_REQ_GET_URL 0x02 + +// WebUSB types +#define WEBUSB_DESC_TYPE_URL 0x03 +#define WEBUSB_URL_SCHEME_HTTPS 0x01 +#define WEBUSB_URL_SCHEME_HTTP 0x00 + +// WinUSB requests +#define WINUSB_REQ_GET_COMPATID_DESCRIPTOR 0x04 +#define WINUSB_REQ_GET_EXT_PROPS_OS 0x05 +#define WINUSB_REQ_GET_DESCRIPTOR 0x07 + +#define STS_GOUT_NAK 1 +#define STS_DATA_UPDT 2 +#define STS_XFER_COMP 3 +#define STS_SETUP_COMP 4 +#define STS_SETUP_UPDT 6 + +uint8_t response[USBPACKET_MAX_SIZE]; + +// for the repeating interfaces +#define DSCR_INTERFACE_LEN 9 +#define DSCR_ENDPOINT_LEN 7 +#define DSCR_CONFIG_LEN 9 +#define DSCR_DEVICE_LEN 18 + +// endpoint types +#define ENDPOINT_TYPE_CONTROL 0 +#define ENDPOINT_TYPE_ISO 1 +#define ENDPOINT_TYPE_BULK 2 +#define ENDPOINT_TYPE_INT 3 + +// These are arbitrary values used in bRequest +#define MS_VENDOR_CODE 0x20 +#define WEBUSB_VENDOR_CODE 0x30 + +// BOS constants +#define BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH 0x05 +#define BINARY_OBJECT_STORE_DESCRIPTOR 0x0F +#define WINUSB_PLATFORM_DESCRIPTOR_LENGTH 0x9E + +// Convert machine byte order to USB byte order +#define TOUSBORDER(num)\ + ((num) & 0xFFU), (((uint16_t)(num) >> 8) & 0xFFU) + +// take in string length and return the first 2 bytes of a string descriptor +#define STRING_DESCRIPTOR_HEADER(size)\ + (((((size) * 2) + 2) & 0xFF) | 0x0300) + +uint8_t device_desc[] = { + DSCR_DEVICE_LEN, USB_DESC_TYPE_DEVICE, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // Class, Subclass, Protocol, Max Packet Size + TOUSBORDER(USB_VID), // idVendor + TOUSBORDER(USB_PID), // idProduct + 0x00, 0x00, // bcdDevice + 0x01, 0x02, // Manufacturer, Product + 0x03, 0x01 // Serial Number, Num Configurations +}; + +uint8_t device_qualifier[] = { + 0x0a, USB_DESC_TYPE_DEVICE_QUALIFIER, //Length, Type + 0x10, 0x02, // bcdUSB max version of USB supported (2.1) + 0xFF, 0xFF, 0xFF, 0x40, // bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0 + 0x01, 0x00 // bNumConfigurations, bReserved +}; + +#define ENDPOINT_RCV 0x80 +#define ENDPOINT_SND 0x00 + +uint8_t configuration_desc[] = { + DSCR_CONFIG_LEN, USB_DESC_TYPE_CONFIGURATION, // Length, Type, + TOUSBORDER(0x0045U), // Total Len (uint16) + 0x01, 0x01, STRING_OFFSET_ICONFIGURATION, // Num Interface, Config Value, Configuration + 0xc0, 0x32, // Attributes, Max Power + // interface 0 ALT 0 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x00, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval (NA) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // interface 0 ALT 1 + DSCR_INTERFACE_LEN, USB_DESC_TYPE_INTERFACE, // Length, Type + 0x00, 0x01, 0x03, // Index, Alt Index idx, Endpoint count + 0XFF, 0xFF, 0xFF, // Class, Subclass, Protocol + 0x00, // Interface + // endpoint 1, read CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_RCV | 1, ENDPOINT_TYPE_INT, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x05, // Polling Interval (5 frames) + // endpoint 2, send serial + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 2, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval + // endpoint 3, send CAN + DSCR_ENDPOINT_LEN, USB_DESC_TYPE_ENDPOINT, // Length, Type + ENDPOINT_SND | 3, ENDPOINT_TYPE_BULK, // Endpoint Num/Direction, Type + TOUSBORDER(0x0040U), // Max Packet (0x0040) + 0x00, // Polling Interval +}; + +// STRING_DESCRIPTOR_HEADER is for uint16 string descriptors +// it takes in a string length, which is bytes/2 because unicode +uint16_t string_language_desc[] = { + STRING_DESCRIPTOR_HEADER(1), + 0x0409 // american english +}; + +// these strings are all uint16's so that we don't need to spam ,0 after every character +uint16_t string_manufacturer_desc[] = { + STRING_DESCRIPTOR_HEADER(8), + 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' +}; + +uint16_t string_product_desc[] = { + STRING_DESCRIPTOR_HEADER(5), + 'p', 'a', 'n', 'd', 'a' +}; + +// default serial number when we're not a panda +uint16_t string_serial_desc[] = { + STRING_DESCRIPTOR_HEADER(4), + 'n', 'o', 'n', 'e' +}; + +// a string containing the default configuration index +uint16_t string_configuration_desc[] = { + STRING_DESCRIPTOR_HEADER(2), + '0', '1' // "01" +}; + +// WCID (auto install WinUSB driver) +// https://github.com/pbatard/libwdi/wiki/WCID-Devices +// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation#automatic-installation-of--winusb-without-an-inf-file +// WinUSB 1.0 descriptors, this is mostly used by Windows XP +uint8_t string_238_desc[] = { + 0x12, USB_DESC_TYPE_STRING, // bLength, bDescriptorType + 'M',0, 'S',0, 'F',0, 'T',0, '1',0, '0',0, '0',0, // qwSignature (MSFT100) + MS_VENDOR_CODE, 0x00 // bMS_VendorCode, bPad +}; +uint8_t winusb_ext_compatid_os_desc[] = { + 0x28, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x04, 0x00, // wIndex + 0x01, // bCount + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + 0x00, // bFirstInterfaceNumber + 0x00, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subcompatible ID (none) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved +}; +uint8_t winusb_ext_prop_os_desc[] = { + 0x8e, 0x00, 0x00, 0x00, // dwLength + 0x00, 0x01, // bcdVersion + 0x05, 0x00, // wIndex + 0x01, 0x00, // wCount + // first property + 0x84, 0x00, 0x00, 0x00, // dwSize + 0x01, 0x00, 0x00, 0x00, // dwPropertyDataType + 0x28, 0x00, // wPropertyNameLength + 'D',0, 'e',0, 'v',0, 'i',0, 'c',0, 'e',0, 'I',0, 'n',0, 't',0, 'e',0, 'r',0, 'f',0, 'a',0, 'c',0, 'e',0, 'G',0, 'U',0, 'I',0, 'D',0, 0, 0, // bPropertyName (DeviceInterfaceGUID) + 0x4e, 0x00, 0x00, 0x00, // dwPropertyDataLength + '{',0, 'c',0, 'c',0, 'e',0, '5',0, '2',0, '9',0, '1',0, 'c',0, '-',0, 'a',0, '6',0, '9',0, 'f',0, '-',0, '4',0 ,'9',0 ,'9',0 ,'5',0 ,'-',0, 'a',0, '4',0, 'c',0, '2',0, '-',0, '2',0, 'a',0, 'e',0, '5',0, '7',0, 'a',0, '5',0, '1',0, 'a',0, 'd',0, 'e',0, '9',0, '}',0, 0, 0, // bPropertyData ({CCE5291C-A69F-4995-A4C2-2AE57A51ADE9}) +}; + +/* +Binary Object Store descriptor used to expose WebUSB (and more WinUSB) metadata +comments are from the wicg spec +References used: + https://wicg.github.io/webusb/#webusb-platform-capability-descriptor + https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c + https://os.mbed.com/users/larsgk/code/USBDevice_WebUSB/file/1d8a6665d607/WebUSBDevice/ + +*/ +uint8_t binary_object_store_desc[] = { + // BOS header + BINARY_OBJECT_STORE_DESCRIPTOR_LENGTH, // bLength, this is only the length of the header + BINARY_OBJECT_STORE_DESCRIPTOR, // bDescriptorType + 0x39, 0x00, // wTotalLength (LSB, MSB) + 0x02, // bNumDeviceCaps (WebUSB + WinUSB) + + // ------------------------------------------------- + // WebUSB descriptor + // header + 0x18, // bLength, Size of this descriptor. Must be set to 24. + 0x10, // bDescriptorType, DEVICE CAPABILITY descriptor + 0x05, // bDevCapabilityType, PLATFORM capability + 0x00, // bReserved, This field is reserved and shall be set to zero. + + // PlatformCapabilityUUID, Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}. + 0x38, 0xB6, 0x08, 0x34, + 0xA9, 0x09, 0xA0, 0x47, + 0x8B, 0xFD, 0xA0, 0x76, + 0x88, 0x15, 0xB6, 0x65, + // + + 0x00, 0x01, // bcdVersion, Protocol version supported. Must be set to 0x0100. + WEBUSB_VENDOR_CODE, // bVendorCode, bRequest value used for issuing WebUSB requests. + // there used to be a concept of "allowed origins", but it was removed from the spec + // it was intended to be a security feature, but then the entire security model relies on domain ownership + // https://github.com/WICG/webusb/issues/49 + // other implementations use various other indexed to leverate this no-longer-valid feature. we wont. + // the spec says we *must* reply to index 0x03 with the url, so we'll hint that that's the right index + 0x03, // iLandingPage, URL descriptor index of the device’s landing page. + + // ------------------------------------------------- + // WinUSB descriptor + // header + 0x1C, // Descriptor size (28 bytes) + 0x10, // Descriptor type (Device Capability) + 0x05, // Capability type (Platform) + 0x00, // Reserved + + // MS OS 2.0 Platform Capability ID (D8DD60DF-4589-4CC7-9CD2-659D9E648A9F) + // Indicates the device supports the Microsoft OS 2.0 descriptor + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, 0xC7, 0x4C, + 0x9C, 0xD2, 0x65, 0x9D, + 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, // Windows version, currently set to 8.1 (0x06030000) + + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // MS OS 2.0 descriptor size (word) + MS_VENDOR_CODE, 0x00 // vendor code, no alternate enumeration +}; + +uint8_t webusb_url_descriptor[] = { + 0x14, /* bLength */ + WEBUSB_DESC_TYPE_URL, // bDescriptorType + WEBUSB_URL_SCHEME_HTTPS, // bScheme + 'u', 's', 'b', 'p', 'a', 'n', 'd', 'a', '.', 'c', 'o', 'm', 'm', 'a', '.', 'a', 'i' +}; + +// WinUSB 2.0 descriptor. This is what modern systems use +// https://github.com/sowbug/weblight/blob/192ad7a0e903542e2aa28c607d98254a12a6399d/firmware/webusb.c +// http://janaxelson.com/files/ms_os_20_descriptors.c +// https://books.google.com/books?id=pkefBgAAQBAJ&pg=PA353&lpg=PA353 +uint8_t winusb_20_desc[WINUSB_PLATFORM_DESCRIPTOR_LENGTH] = { + // Microsoft OS 2.0 descriptor set header (table 10) + 0x0A, 0x00, // Descriptor size (10 bytes) + 0x00, 0x00, // MS OS 2.0 descriptor set header + + 0x00, 0x00, 0x03, 0x06, // Windows version (8.1) (0x06030000) + WINUSB_PLATFORM_DESCRIPTOR_LENGTH, 0x00, // Total size of MS OS 2.0 descriptor set + + // Microsoft OS 2.0 compatible ID descriptor + 0x14, 0x00, // Descriptor size (20 bytes) + 0x03, 0x00, // MS OS 2.0 compatible ID descriptor + 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00, // compatible ID (WINUSB) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-compatible ID + + // Registry property descriptor + 0x80, 0x00, // Descriptor size (130 bytes) + 0x04, 0x00, // Registry Property descriptor + 0x01, 0x00, // Strings are null-terminated Unicode + 0x28, 0x00, // Size of Property Name (40 bytes) "DeviceInterfaceGUID" + + // bPropertyName (DeviceInterfaceGUID) + 'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, + 't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, + 'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, + + 0x4E, 0x00, // Size of Property Data (78 bytes) + + // Vendor-defined property data: {CCE5291C-A69F-4995-A4C2-2AE57A51ADE9} + '{', 0x00, 'c', 0x00, 'c', 0x00, 'e', 0x00, '5', 0x00, '2', 0x00, '9', 0x00, '1', 0x00, // 16 + 'c', 0x00, '-', 0x00, 'a', 0x00, '6', 0x00, '9', 0x00, 'f', 0x00, '-', 0x00, '4', 0x00, // 32 + '9', 0x00, '9', 0x00, '5', 0x00, '-', 0x00, 'a', 0x00, '4', 0x00, 'c', 0x00, '2', 0x00, // 48 + '-', 0x00, '2', 0x00, 'a', 0x00, 'e', 0x00, '5', 0x00, '7', 0x00, 'a', 0x00, '5', 0x00, // 64 + '1', 0x00, 'a', 0x00, 'd', 0x00, 'e', 0x00, '9', 0x00, '}', 0x00, 0x00, 0x00 // 78 bytes +}; + +// current packet +USB_Setup_TypeDef setup; +uint8_t usbdata[0x100] __attribute__((aligned(4))); +uint8_t* ep0_txdata = NULL; +uint16_t ep0_txlen = 0; +bool outep3_processing = false; + +// Store the current interface alt setting. +int current_int0_alt_setting = 0; + +// packet read and write + +void *USB_ReadPacket(void *dest, uint16_t len) { + uint32_t *dest_copy = (uint32_t *)dest; + uint32_t count32b = ((uint32_t)len + 3U) / 4U; + + for (uint32_t i = 0; i < count32b; i++) { + *dest_copy = USBx_DFIFO(0); + dest_copy++; + } + return ((void *)dest_copy); +} + +void USB_WritePacket(const void *src, uint16_t len, uint32_t ep) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint32_t numpacket = ((uint32_t)len + (USBPACKET_MAX_SIZE - 1U)) / USBPACKET_MAX_SIZE; + uint32_t count32b = 0; + count32b = ((uint32_t)len + 3U) / 4U; + + // TODO: revisit this + USBx_INEP(ep)->DIEPTSIZ = ((numpacket << 19) & USB_OTG_DIEPTSIZ_PKTCNT) | + (len & USB_OTG_DIEPTSIZ_XFRSIZ); + USBx_INEP(ep)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA); + + // load the FIFO + if (src != NULL) { + const uint32_t *src_copy = (const uint32_t *)src; + for (uint32_t i = 0; i < count32b; i++) { + USBx_DFIFO(ep) = *src_copy; + src_copy++; + } + } +} + +// IN EP 0 TX FIFO has a max size of 127 bytes (much smaller than the rest) +// so use TX FIFO empty interrupt to send larger amounts of data +void USB_WritePacket_EP0(uint8_t *src, uint16_t len) { + #ifdef DEBUG_USB + print("writing "); + hexdump(src, len); + #endif + + uint16_t wplen = MIN(len, 0x40); + USB_WritePacket(src, wplen, 0); + + if (wplen < len) { + ep0_txdata = &src[wplen]; + ep0_txlen = len - wplen; + USBx_DEVICE->DIEPEMPMSK |= 1; + } else { + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } +} + +void usb_reset(void) { + // unmask endpoint interrupts, so many sets + USBx_DEVICE->DAINT = 0xFFFFFFFFU; + USBx_DEVICE->DAINTMSK = 0xFFFFFFFFU; + //USBx_DEVICE->DOEPMSK = (USB_OTG_DOEPMSK_STUPM | USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_EPDM); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM | USB_OTG_DIEPMSK_ITTXFEMSK); + //USBx_DEVICE->DIEPMSK = (USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_EPDM); + + // all interrupts for debugging + USBx_DEVICE->DIEPMSK = 0xFFFFFFFFU; + USBx_DEVICE->DOEPMSK = 0xFFFFFFFFU; + + // clear interrupts + USBx_INEP(0)->DIEPINT = 0xFF; + USBx_OUTEP(0)->DOEPINT = 0xFF; + + // unset the address + USBx_DEVICE->DCFG &= ~USB_OTG_DCFG_DAD; + + // set up USB FIFOs + // RX start address is fixed to 0 + USBx->GRXFSIZ = 0x40; + + // 0x100 to offset past GRXFSIZ + USBx->DIEPTXF0_HNPTXFSIZ = (0x40UL << 16) | 0x40U; + + // EP1, massive + USBx->DIEPTXF[0] = (0x40UL << 16) | 0x80U; + + // flush TX fifo + USBx->GRSTCTL = USB_OTG_GRSTCTL_TXFFLSH | USB_OTG_GRSTCTL_TXFNUM_4; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); + // flush RX FIFO + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); + + // no global NAK + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGINAK; + + // ready to receive setup packets + USBx_OUTEP(0)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (3U << 3); +} + +char to_hex_char(uint8_t a) { + char ret; + if (a < 10U) { + ret = '0' + a; + } else { + ret = 'a' + (a - 10U); + } + return ret; +} + +void usb_tick(void) { + uint16_t current_frame_num = (USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF_Msk) >> USB_OTG_DSTS_FNSOF_Pos; + usb_enumerated = (current_frame_num != usb_last_frame_num); + usb_last_frame_num = current_frame_num; +} + +void usb_setup(void) { + int resp_len; + ControlPacket_t control_req; + + // setup packet is ready + switch (setup.b.bRequest) { + case USB_REQ_SET_CONFIGURATION: + // enable other endpoints, has to be here? + USBx_INEP(1)->DIEPCTL = (0x40U & USB_OTG_DIEPCTL_MPSIZ) | (2UL << 18) | (1UL << 22) | + USB_OTG_DIEPCTL_SD0PID_SEVNFRM | USB_OTG_DIEPCTL_USBAEP; + USBx_INEP(1)->DIEPINT = 0xFF; + + USBx_OUTEP(2)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(2)->DOEPINT = 0xFF; + + USBx_OUTEP(3)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2UL << 18) | + USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; + USBx_OUTEP(3)->DOEPINT = 0xFF; + + // mark ready to receive + USBx_OUTEP(2)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_ADDRESS: + // set now? + USBx_DEVICE->DCFG |= ((setup.b.wValue.w & 0x7fU) << 4); + + #ifdef DEBUG_USB + print(" set address\n"); + #endif + + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + break; + case USB_REQ_GET_DESCRIPTOR: + switch (setup.b.wValue.bw.lsb) { + case USB_DESC_TYPE_DEVICE: + //print(" writing device descriptor\n"); + + // set bcdDevice to hardware type + device_desc[13] = hw_type; + // setup transfer + USB_WritePacket(device_desc, MIN(sizeof(device_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + + //print("D"); + break; + case USB_DESC_TYPE_CONFIGURATION: + USB_WritePacket(configuration_desc, MIN(sizeof(configuration_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_DEVICE_QUALIFIER: + USB_WritePacket(device_qualifier, MIN(sizeof(device_qualifier), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_STRING: + switch (setup.b.wValue.bw.msb) { + case STRING_OFFSET_LANGID: + USB_WritePacket((uint8_t*)string_language_desc, MIN(sizeof(string_language_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IMANUFACTURER: + USB_WritePacket((uint8_t*)string_manufacturer_desc, MIN(sizeof(string_manufacturer_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_IPRODUCT: + USB_WritePacket((uint8_t*)string_product_desc, MIN(sizeof(string_product_desc), setup.b.wLength.w), 0); + break; + case STRING_OFFSET_ISERIAL: + #ifdef UID_BASE + response[0] = 0x02 + (12 * 4); + response[1] = 0x03; + + // 96 bits = 12 bytes + for (int i = 0; i < 12; i++){ + uint8_t cc = ((uint8_t *)UID_BASE)[i]; + response[2 + (i * 4)] = to_hex_char((cc >> 4) & 0xFU); + response[2 + (i * 4) + 1] = '\0'; + response[2 + (i * 4) + 2] = to_hex_char((cc >> 0) & 0xFU); + response[2 + (i * 4) + 3] = '\0'; + } + + USB_WritePacket(response, MIN(response[0], setup.b.wLength.w), 0); + #else + USB_WritePacket((const uint8_t *)string_serial_desc, MIN(sizeof(string_serial_desc), setup.b.wLength.w), 0); + #endif + break; + case STRING_OFFSET_ICONFIGURATION: + USB_WritePacket((uint8_t*)string_configuration_desc, MIN(sizeof(string_configuration_desc), setup.b.wLength.w), 0); + break; + case 238: + USB_WritePacket((uint8_t*)string_238_desc, MIN(sizeof(string_238_desc), setup.b.wLength.w), 0); + break; + default: + // nothing + USB_WritePacket(0, 0, 0); + break; + } + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_DESC_TYPE_BINARY_OBJECT_STORE: + USB_WritePacket(binary_object_store_desc, MIN(sizeof(binary_object_store_desc), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // nothing here? + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case USB_REQ_GET_STATUS: + // empty response? + response[0] = 0; + response[1] = 0; + USB_WritePacket((void*)&response, 2, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case USB_REQ_SET_INTERFACE: + // Store the alt setting number for IN EP behavior. + current_int0_alt_setting = setup.b.wValue.w; + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + case WEBUSB_VENDOR_CODE: + switch (setup.b.wIndex.w) { + case WEBUSB_REQ_GET_URL: + USB_WritePacket(webusb_url_descriptor, MIN(sizeof(webusb_url_descriptor), setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + default: + // probably asking for allowed origins, which was removed from the spec + USB_WritePacket(0, 0, 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + break; + } + break; + case MS_VENDOR_CODE: + switch (setup.b.wIndex.w) { + // winusb 2.0 descriptor from BOS + case WINUSB_REQ_GET_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_20_desc, MIN(sizeof(winusb_20_desc), setup.b.wLength.w)); + break; + // Extended Compat ID OS Descriptor + case WINUSB_REQ_GET_COMPATID_DESCRIPTOR: + USB_WritePacket_EP0((uint8_t*)winusb_ext_compatid_os_desc, MIN(sizeof(winusb_ext_compatid_os_desc), setup.b.wLength.w)); + break; + // Extended Properties OS Descriptor + case WINUSB_REQ_GET_EXT_PROPS_OS: + USB_WritePacket_EP0((uint8_t*)winusb_ext_prop_os_desc, MIN(sizeof(winusb_ext_prop_os_desc), setup.b.wLength.w)); + break; + default: + USB_WritePacket_EP0(0, 0); + } + break; + default: + control_req.request = setup.b.bRequest; + control_req.param1 = setup.b.wValue.w; + control_req.param2 = setup.b.wIndex.w; + control_req.length = setup.b.wLength.w; + + resp_len = comms_control_handler(&control_req, response); + // response pending if -1 was returned + if (resp_len != -1) { + USB_WritePacket(response, MIN(resp_len, setup.b.wLength.w), 0); + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + } +} + + + +// ***************************** USB port ***************************** + +void usb_irqhandler(void) { + //USBx->GINTMSK = 0; + + unsigned int gintsts = USBx->GINTSTS; + unsigned int gotgint = USBx->GOTGINT; + unsigned int daint = USBx_DEVICE->DAINT; + + // gintsts SUSPEND? 04008428 + #ifdef DEBUG_USB + puth(gintsts); + print(" "); + /*puth(USBx->GCCFG); + print(" ");*/ + puth(gotgint); + print(" ep "); + puth(daint); + print(" USB interrupt!\n"); + #endif + + if ((gintsts & USB_OTG_GINTSTS_CIDSCHG) != 0) { + print("connector ID status change\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_USBRST) != 0) { + print("USB reset\n"); + usb_reset(); + } + + if ((gintsts & USB_OTG_GINTSTS_ENUMDNE) != 0) { + print("enumeration done"); + // Full speed, ENUMSPD + //puth(USBx_DEVICE->DSTS); + print("\n"); + } + + if ((gintsts & USB_OTG_GINTSTS_OTGINT) != 0) { + print("OTG int:"); + puth(USBx->GOTGINT); + print("\n"); + + // getting ADTOCHG + //USBx->GOTGINT = USBx->GOTGINT; + } + + // RX FIFO first + if ((gintsts & USB_OTG_GINTSTS_RXFLVL) != 0) { + // 1. Read the Receive status pop register + volatile unsigned int rxst = USBx->GRXSTSP; + int status = (rxst & USB_OTG_GRXSTSP_PKTSTS) >> 17; + + #ifdef DEBUG_USB + print(" RX FIFO:"); + puth(rxst); + print(" status: "); + puth(status); + print(" len: "); + puth((rxst & USB_OTG_GRXSTSP_BCNT) >> 4); + print("\n"); + #endif + + if (status == STS_DATA_UPDT) { + int endpoint = (rxst & USB_OTG_GRXSTSP_EPNUM); + int len = (rxst & USB_OTG_GRXSTSP_BCNT) >> 4; + (void)USB_ReadPacket(&usbdata, len); + #ifdef DEBUG_USB + print(" data "); + puth(len); + print("\n"); + hexdump(&usbdata, len); + #endif + + if (endpoint == 2) { + comms_endpoint2_write((uint8_t *) usbdata, len); + } + + if (endpoint == 3) { + outep3_processing = true; + comms_can_write(usbdata, len); + } + } else if (status == STS_SETUP_UPDT) { + (void)USB_ReadPacket(&setup, 8); + #ifdef DEBUG_USB + print(" setup "); + hexdump(&setup, 8); + print("\n"); + #endif + } else { + // status is neither STS_DATA_UPDT or STS_SETUP_UPDT, skip + } + } + + /*if (gintsts & USB_OTG_GINTSTS_HPRTINT) { + // host + print("HPRT:"); + puth(USBx_HOST_PORT->HPRT); + print("\n"); + if (USBx_HOST_PORT->HPRT & USB_OTG_HPRT_PCDET) { + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PRST; + USBx_HOST_PORT->HPRT |= USB_OTG_HPRT_PCDET; + } + + }*/ + + if ((gintsts & USB_OTG_GINTSTS_BOUTNAKEFF) || (gintsts & USB_OTG_GINTSTS_GINAKEFF)) { + // no global NAK, why is this getting set? + #ifdef DEBUG_USB + print("GLOBAL NAK\n"); + #endif + USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK | USB_OTG_DCTL_CGINAK; + } + + if ((gintsts & USB_OTG_GINTSTS_SRQINT) != 0) { + // we want to do "A-device host negotiation protocol" since we are the A-device + /*print("start request\n"); + puth(USBx->GOTGCTL); + print("\n");*/ + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + //USBx_HOST_PORT->HPRT = USB_OTG_HPRT_PPWR | USB_OTG_HPRT_PENA; + //USBx->GOTGCTL |= USB_OTG_GOTGCTL_SRQ; + } + + // out endpoint hit + if ((gintsts & USB_OTG_GINTSTS_OEPINT) != 0) { + #ifdef DEBUG_USB + print(" 0:"); + puth(USBx_OUTEP(0)->DOEPINT); + print(" 2:"); + puth(USBx_OUTEP(2)->DOEPINT); + print(" 3:"); + puth(USBx_OUTEP(3)->DOEPINT); + print(" "); + puth(USBx_OUTEP(3)->DOEPCTL); + print(" 4:"); + puth(USBx_OUTEP(4)->DOEPINT); + print(" OUT ENDPOINT\n"); + #endif + + if ((USBx_OUTEP(2)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0) { + #ifdef DEBUG_USB + print(" OUT2 PACKET XFRC\n"); + #endif + USBx_OUTEP(2)->DOEPTSIZ = (1UL << 19) | 0x40U; + USBx_OUTEP(2)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + + if ((USBx_OUTEP(3)->DOEPINT & USB_OTG_DOEPINT_XFRC) != 0) { + #ifdef DEBUG_USB + print(" OUT3 PACKET XFRC\n"); + #endif + // NAK cleared by process_can (if tx buffers have room) + outep3_processing = false; + refresh_can_tx_slots_available(); + } else if ((USBx_OUTEP(3)->DOEPINT & 0x2000) != 0) { + #ifdef DEBUG_USB + print(" OUT3 PACKET WTF\n"); + #endif + // if NAK was set trigger this, unknown interrupt + // TODO: why was this here? fires when TX buffers when we can't clear NAK + // USBx_OUTEP(3)->DOEPTSIZ = (1U << 19) | 0x40U; + // USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } else if ((USBx_OUTEP(3)->DOEPINT) != 0) { + #ifdef DEBUG_USB + print("OUTEP3 error "); + puth(USBx_OUTEP(3)->DOEPINT); + print("\n"); + #endif + } else { + // USBx_OUTEP(3)->DOEPINT is 0, ok to skip + } + + if ((USBx_OUTEP(0)->DOEPINT & USB_OTG_DIEPINT_XFRC) != 0) { + // ready for next packet + USBx_OUTEP(0)->DOEPTSIZ = USB_OTG_DOEPTSIZ_STUPCNT | (USB_OTG_DOEPTSIZ_PKTCNT & (1UL << 19)) | (1U << 3); + } + + // respond to setup packets + if ((USBx_OUTEP(0)->DOEPINT & USB_OTG_DOEPINT_STUP) != 0) { + usb_setup(); + } + + USBx_OUTEP(0)->DOEPINT = USBx_OUTEP(0)->DOEPINT; + USBx_OUTEP(2)->DOEPINT = USBx_OUTEP(2)->DOEPINT; + USBx_OUTEP(3)->DOEPINT = USBx_OUTEP(3)->DOEPINT; + } + + // interrupt endpoint hit (Page 1221) + if ((gintsts & USB_OTG_GINTSTS_IEPINT) != 0) { + #ifdef DEBUG_USB + print(" "); + puth(USBx_INEP(0)->DIEPINT); + print(" "); + puth(USBx_INEP(1)->DIEPINT); + print(" IN ENDPOINT\n"); + #endif + + // Should likely check the EP of the IN request even if there is + // only one IN endpoint. + + // No need to set NAK in OTG_DIEPCTL0 when nothing to send, + // Appears USB core automatically sets NAK. WritePacket clears it. + + // Handle the two interface alternate settings. Setting 0 has EP1 + // as bulk. Setting 1 has EP1 as interrupt. The code to handle + // these two EP variations are very similar and can be + // restructured for smaller code footprint. Keeping split out for + // now for clarity. + + //TODO add default case. Should it NAK? + switch (current_int0_alt_setting) { + case 0: ////// Bulk config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + USB_WritePacket((void *)response, comms_can_read(response, 0x40), 1); + } + break; + + case 1: ////// Interrupt config + // *** IN token received when TxFIFO is empty + if ((USBx_INEP(1)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + // TODO: always assuming max len, can we get the length? + int len = comms_can_read(response, 0x40); + if (len > 0) { + USB_WritePacket((void *)response, len, 1); + } + } + break; + default: + print("current_int0_alt_setting value invalid\n"); + break; + } + + if ((USBx_INEP(0)->DIEPINT & USB_OTG_DIEPMSK_ITTXFEMSK) != 0) { + #ifdef DEBUG_USB + print(" IN PACKET QUEUE\n"); + #endif + + if ((ep0_txlen != 0U) && ((USBx_INEP(0)->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV) >= 0x40U)) { + uint16_t len = MIN(ep0_txlen, 0x40); + USB_WritePacket(ep0_txdata, len, 0); + ep0_txdata = &ep0_txdata[len]; + ep0_txlen -= len; + if (ep0_txlen == 0U) { + ep0_txdata = NULL; + USBx_DEVICE->DIEPEMPMSK &= ~1; + USBx_OUTEP(0)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; + } + } + } + + // clear interrupts + USBx_INEP(0)->DIEPINT = USBx_INEP(0)->DIEPINT; // Why ep0? + USBx_INEP(1)->DIEPINT = USBx_INEP(1)->DIEPINT; + } + + // clear all interrupts we handled + USBx_DEVICE->DAINT = daint; + USBx->GOTGINT = gotgint; + USBx->GINTSTS = gintsts; + + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); +} + +void can_tx_comms_resume_usb(void) { + ENTER_CRITICAL(); + if (!outep3_processing && (USBx_OUTEP(3)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0) { + USBx_OUTEP(3)->DOEPTSIZ = (32UL << 19) | 0x800U; + USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; + } + EXIT_CRITICAL(); +} + +void usb_soft_disconnect(bool enable) { + if (enable) { + USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS; + } else { + USBx_DEVICE->DCTL &= ~USB_OTG_DCTL_SDIS; + } +} diff --git a/panda/board/drivers/watchdog.h b/panda/board/drivers/watchdog.h new file mode 100644 index 0000000..89cf01e --- /dev/null +++ b/panda/board/drivers/watchdog.h @@ -0,0 +1,24 @@ +typedef enum { + WATCHDOG_50_MS = (400U - 1U), + WATCHDOG_500_MS = 4000U, +} WatchdogTimeout; + +void watchdog_feed(void) { + IND_WDG->KR = 0xAAAAU; +} + +void watchdog_init(WatchdogTimeout timeout) { + // enable watchdog + IND_WDG->KR = 0xCCCCU; + IND_WDG->KR = 0x5555U; + + // 32KHz / 4 prescaler = 8000Hz + register_set(&(IND_WDG->PR), 0x0U, IWDG_PR_PR_Msk); + register_set(&(IND_WDG->RLR), timeout, IWDG_RLR_RL_Msk); + + // wait for watchdog to be updated + while (IND_WDG->SR != 0U); + + // start the countdown + watchdog_feed(); +} diff --git a/panda/board/early_init.h b/panda/board/early_init.h new file mode 100644 index 0000000..ae652ae --- /dev/null +++ b/panda/board/early_init.h @@ -0,0 +1,60 @@ +// Early bringup +#define ENTER_BOOTLOADER_MAGIC 0xdeadbeefU +#define ENTER_SOFTLOADER_MAGIC 0xdeadc0deU +#define BOOT_NORMAL 0xdeadb111U + +extern void *g_pfnVectors; +extern uint32_t enter_bootloader_mode; + +void jump_to_bootloader(void) { + // do enter bootloader + enter_bootloader_mode = 0; + void (*bootloader)(void) = (void (*)(void)) (*((uint32_t *)BOOTLOADER_ADDRESS)); + + // jump to bootloader + enable_interrupts(); + bootloader(); + + // reset on exit + enter_bootloader_mode = BOOT_NORMAL; + NVIC_SystemReset(); +} + +void early_initialization(void) { + // Reset global critical depth + disable_interrupts(); + global_critical_depth = 0; + + // Init register and interrupt tables + init_registers(); + + // after it's been in the bootloader, things are initted differently, so we reset + if ((enter_bootloader_mode != BOOT_NORMAL) && + (enter_bootloader_mode != ENTER_BOOTLOADER_MAGIC) && + (enter_bootloader_mode != ENTER_SOFTLOADER_MAGIC)) { + enter_bootloader_mode = BOOT_NORMAL; + NVIC_SystemReset(); + } + + // if wrong chip, reboot + volatile unsigned int id = DBGMCU->IDCODE; + if ((id & 0xFFFU) != MCU_IDCODE) { + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + } + + // setup interrupt table + SCB->VTOR = (uint32_t)&g_pfnVectors; + + // early GPIOs float everything + early_gpio_float(); + + detect_board_type(); + + if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { + #ifdef PANDA + current_board->init_bootloader(); + #endif + current_board->set_led(LED_GREEN, 1); + jump_to_bootloader(); + } +} diff --git a/panda/board/fake_stm.h b/panda/board/fake_stm.h new file mode 100644 index 0000000..b73a4e8 --- /dev/null +++ b/panda/board/fake_stm.h @@ -0,0 +1,33 @@ +// minimal code to fake a panda for tests +#include +#include +#include + +#include "utils.h" + +#define CANFD +#define ALLOW_DEBUG +#define PANDA + +#define ENTER_CRITICAL() 0 +#define EXIT_CRITICAL() 0 + +void print(const char *a) { + printf("%s", a); +} + +void puth(unsigned int i) { + printf("%u", i); +} + +typedef struct { + uint32_t CNT; +} TIM_TypeDef; + +TIM_TypeDef timer; +TIM_TypeDef *MICROSECOND_TIMER = &timer; +uint32_t microsecond_timer_get(void); + +uint32_t microsecond_timer_get(void) { + return MICROSECOND_TIMER->CNT; +} diff --git a/panda/board/faults.h b/panda/board/faults.h new file mode 100644 index 0000000..a7f437d --- /dev/null +++ b/panda/board/faults.h @@ -0,0 +1,59 @@ +#define FAULT_STATUS_NONE 0U +#define FAULT_STATUS_TEMPORARY 1U +#define FAULT_STATUS_PERMANENT 2U + +// Fault types, matches cereal.log.PandaState.FaultType +#define FAULT_RELAY_MALFUNCTION (1UL << 0) +#define FAULT_UNUSED_INTERRUPT_HANDLED (1UL << 1) +#define FAULT_INTERRUPT_RATE_CAN_1 (1UL << 2) +#define FAULT_INTERRUPT_RATE_CAN_2 (1UL << 3) +#define FAULT_INTERRUPT_RATE_CAN_3 (1UL << 4) +#define FAULT_INTERRUPT_RATE_TACH (1UL << 5) +#define FAULT_INTERRUPT_RATE_GMLAN (1UL << 6) +#define FAULT_INTERRUPT_RATE_INTERRUPTS (1UL << 7) +#define FAULT_INTERRUPT_RATE_SPI_DMA (1UL << 8) +#define FAULT_INTERRUPT_RATE_SPI_CS (1UL << 9) +#define FAULT_INTERRUPT_RATE_UART_1 (1UL << 10) +#define FAULT_INTERRUPT_RATE_UART_2 (1UL << 11) +#define FAULT_INTERRUPT_RATE_UART_3 (1UL << 12) +#define FAULT_INTERRUPT_RATE_UART_5 (1UL << 13) +#define FAULT_INTERRUPT_RATE_UART_DMA (1UL << 14) +#define FAULT_INTERRUPT_RATE_USB (1UL << 15) +#define FAULT_INTERRUPT_RATE_TIM1 (1UL << 16) +#define FAULT_INTERRUPT_RATE_TIM3 (1UL << 17) +#define FAULT_REGISTER_DIVERGENT (1UL << 18) +#define FAULT_INTERRUPT_RATE_KLINE_INIT (1UL << 19) +#define FAULT_INTERRUPT_RATE_CLOCK_SOURCE (1UL << 20) +#define FAULT_INTERRUPT_RATE_TICK (1UL << 21) +#define FAULT_INTERRUPT_RATE_EXTI (1UL << 22) +#define FAULT_INTERRUPT_RATE_SPI (1UL << 23) +#define FAULT_INTERRUPT_RATE_UART_7 (1UL << 24) +#define FAULT_SIREN_MALFUNCTION (1UL << 25) +#define FAULT_HEARTBEAT_LOOP_WATCHDOG (1UL << 26) + +// Permanent faults +#define PERMANENT_FAULTS 0U + +uint8_t fault_status = FAULT_STATUS_NONE; +uint32_t faults = 0U; + +void fault_occurred(uint32_t fault) { + if ((faults & fault) == 0U) { + if ((PERMANENT_FAULTS & fault) != 0U) { + print("Permanent fault occurred: 0x"); puth(fault); print("\n"); + fault_status = FAULT_STATUS_PERMANENT; + } else { + print("Temporary fault occurred: 0x"); puth(fault); print("\n"); + fault_status = FAULT_STATUS_TEMPORARY; + } + } + faults |= fault; +} + +void fault_recovered(uint32_t fault) { + if ((PERMANENT_FAULTS & fault) == 0U) { + faults &= ~fault; + } else { + print("Cannot recover from a permanent fault!\n"); + } +} diff --git a/panda/board/flasher.h b/panda/board/flasher.h new file mode 100644 index 0000000..d6c2c40 --- /dev/null +++ b/panda/board/flasher.h @@ -0,0 +1,150 @@ +// flasher state variables +uint32_t *prog_ptr = NULL; +bool unlocked = false; + +void spi_init(void); + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + int resp_len = 0; + + // flasher machine + memset(resp, 0, 4); + memcpy(resp+4, "\xde\xad\xd0\x0d", 4); + resp[0] = 0xff; + resp[2] = req->request; + resp[3] = ~req->request; + *((uint32_t **)&resp[8]) = prog_ptr; + resp_len = 0xc; + + int sec; + switch (req->request) { + // **** 0xb0: flasher echo + case 0xb0: + resp[1] = 0xff; + break; + // **** 0xb1: unlock flash + case 0xb1: + if (flash_is_locked()) { + flash_unlock(); + resp[1] = 0xff; + } + current_board->set_led(LED_GREEN, 1); + unlocked = true; + prog_ptr = (uint32_t *)APP_START_ADDRESS; + break; + // **** 0xb2: erase sector + case 0xb2: + sec = req->param1; + if (flash_erase_sector(sec, unlocked)) { + resp[1] = 0xff; + } + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xd0: fetch serial number + case 0xd0: + // addresses are OTP + if (req->param1 == 1) { + memcpy(resp, (void *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion); + break; + // **** 0xd8: reset ST + case 0xd8: + flush_write_buffer(); + NVIC_SystemReset(); + break; + } + return resp_len; +} + +void comms_can_write(const uint8_t *data, uint32_t len) { + UNUSED(data); + UNUSED(len); +} + +int comms_can_read(uint8_t *data, uint32_t max_len) { + UNUSED(data); + UNUSED(max_len); + return 0; +} + +void refresh_can_tx_slots_available(void) {} + +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + current_board->set_led(LED_RED, 0); + for (uint32_t i = 0; i < len/4; i++) { + flash_write_word(prog_ptr, *(uint32_t*)(data+(i*4))); + + //*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr; + prog_ptr++; + } + current_board->set_led(LED_RED, 1); +} + + +void soft_flasher_start(void) { + print("\n\n\n************************ FLASHER START ************************\n"); + + enter_bootloader_mode = 0; + + flasher_peripherals_init(); + + gpio_usart2_init(); + gpio_usb_init(); + + // enable USB + usb_init(); + + // enable SPI + if (current_board->has_spi) { + gpio_spi_init(); + spi_init(); + } + + // green LED on for flashing + current_board->set_led(LED_GREEN, 1); + + enable_interrupts(); + + for (;;) { + // blink the green LED fast + current_board->set_led(LED_GREEN, 0); + delay(500000); + current_board->set_led(LED_GREEN, 1); + delay(500000); + } +} diff --git a/panda/board/health.h b/panda/board/health.h new file mode 100644 index 0000000..fc7661c --- /dev/null +++ b/panda/board/health.h @@ -0,0 +1,62 @@ +// When changing these structs, python/__init__.py needs to be kept up to date! + +#define HEALTH_PACKET_VERSION 15 +struct __attribute__((packed)) health_t { + uint32_t uptime_pkt; + uint32_t voltage_pkt; + uint32_t current_pkt; + uint32_t safety_tx_blocked_pkt; + uint32_t safety_rx_invalid_pkt; + uint32_t tx_buffer_overflow_pkt; + uint32_t rx_buffer_overflow_pkt; + uint32_t gmlan_send_errs_pkt; + uint32_t faults_pkt; + uint8_t ignition_line_pkt; + uint8_t ignition_can_pkt; + uint8_t controls_allowed_pkt; + uint8_t car_harness_status_pkt; + uint8_t safety_mode_pkt; + uint16_t safety_param_pkt; + uint8_t fault_status_pkt; + uint8_t power_save_enabled_pkt; + uint8_t heartbeat_lost_pkt; + uint16_t alternative_experience_pkt; + float interrupt_load_pkt; + uint8_t fan_power; + uint8_t safety_rx_checks_invalid_pkt; + uint16_t spi_checksum_error_count_pkt; + uint8_t fan_stall_count; + uint16_t sbu1_voltage_mV; + uint16_t sbu2_voltage_mV; + uint8_t som_reset_triggered; +}; + +#define CAN_HEALTH_PACKET_VERSION 5 +typedef struct __attribute__((packed)) { + uint8_t bus_off; + uint32_t bus_off_cnt; + uint8_t error_warning; + uint8_t error_passive; + uint8_t last_error; // real time LEC value + uint8_t last_stored_error; // last LEC positive error code stored + uint8_t last_data_error; // DLEC (for CANFD only) + uint8_t last_data_stored_error; // last DLEC positive error code stored (for CANFD only) + uint8_t receive_error_cnt; // Actual state of the receive error counter, values between 0 and 127. FDCAN_ECR.REC + uint8_t transmit_error_cnt; // Actual state of the transmit error counter, values between 0 and 255. FDCAN_ECR.TEC + uint32_t total_error_cnt; // How many times any error interrupt was invoked + uint32_t total_tx_lost_cnt; // Tx event FIFO element lost + uint32_t total_rx_lost_cnt; // Rx FIFO 0 message lost due to FIFO full condition + uint32_t total_tx_cnt; + uint32_t total_rx_cnt; + uint32_t total_fwd_cnt; // Messages forwarded from one bus to another + uint32_t total_tx_checksum_error_cnt; + uint16_t can_speed; + uint16_t can_data_speed; + uint8_t canfd_enabled; + uint8_t brs_enabled; + uint8_t canfd_non_iso; + uint32_t irq0_call_rate; + uint32_t irq1_call_rate; + uint32_t irq2_call_rate; + uint32_t can_core_reset_cnt; +} can_health_t; diff --git a/panda/board/jungle/SConscript b/panda/board/jungle/SConscript new file mode 100644 index 0000000..3e40611 --- /dev/null +++ b/panda/board/jungle/SConscript @@ -0,0 +1,18 @@ +import os +import copy + +Import('build_project', 'base_project_f4', 'base_project_h7') + +build_projects = { + "panda_jungle": base_project_f4, + "panda_jungle_h7": base_project_h7, +} + +for project_name, project in build_projects.items(): + flags = [ + "-DPANDA_JUNGLE", + ] + if os.getenv("FINAL_PROVISIONING"): + flags += ["-DFINAL_PROVISIONING"] + + build_project(project_name, project, flags) diff --git a/panda/board/jungle/__init__.py b/panda/board/jungle/__init__.py index 77a6e3c..a95ddfa 100644 --- a/panda/board/jungle/__init__.py +++ b/panda/board/jungle/__init__.py @@ -2,7 +2,6 @@ import os import struct from functools import wraps -from typing import Optional from panda import Panda, PandaDFU from panda.python.constants import McuType @@ -57,7 +56,7 @@ class PandaJungle(Panda): fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn.replace("panda", "panda_jungle")) super().flash(fn=fn, code=code, reconnect=reconnect) - def recover(self, timeout: Optional[int] = 60, reset: bool = True) -> bool: + def recover(self, timeout: int | None = 60, reset: bool = True) -> bool: dfu_serial = self.get_dfu_serial() if reset: diff --git a/panda/board/jungle/boards/board_declarations.h b/panda/board/jungle/boards/board_declarations.h new file mode 100644 index 0000000..9259189 --- /dev/null +++ b/panda/board/jungle/boards/board_declarations.h @@ -0,0 +1,82 @@ +// ******************** Prototypes ******************** +typedef void (*board_init)(void); +typedef void (*board_set_led)(uint8_t color, bool enabled); +typedef void (*board_board_tick)(void); +typedef bool (*board_get_button)(void); +typedef void (*board_set_panda_power)(bool enabled); +typedef void (*board_set_panda_individual_power)(uint8_t port_num, bool enabled); +typedef void (*board_set_ignition)(bool enabled); +typedef void (*board_set_individual_ignition)(uint8_t bitmask); +typedef void (*board_set_harness_orientation)(uint8_t orientation); +typedef void (*board_set_can_mode)(uint8_t mode); +typedef void (*board_enable_can_transciever)(uint8_t transciever, bool enabled); +typedef void (*board_enable_header_pin)(uint8_t pin_num, bool enabled); +typedef float (*board_get_channel_power)(uint8_t channel); +typedef uint16_t (*board_get_sbu_mV)(uint8_t channel, uint8_t sbu); + +struct board { + const bool has_canfd; + const bool has_sbu_sense; + const uint16_t avdd_mV; + board_init init; + board_set_led set_led; + board_board_tick board_tick; + board_get_button get_button; + board_set_panda_power set_panda_power; + board_set_panda_individual_power set_panda_individual_power; + board_set_ignition set_ignition; + board_set_individual_ignition set_individual_ignition; + board_set_harness_orientation set_harness_orientation; + board_set_can_mode set_can_mode; + board_enable_can_transciever enable_can_transciever; + board_enable_header_pin enable_header_pin; + board_get_channel_power get_channel_power; + board_get_sbu_mV get_sbu_mV; + + // TODO: shouldn't need these + bool has_spi; +}; + +// ******************* Definitions ******************** +#define HW_TYPE_UNKNOWN 0U +#define HW_TYPE_V1 1U +#define HW_TYPE_V2 2U + +// LED colors +#define LED_RED 0U +#define LED_GREEN 1U +#define LED_BLUE 2U + +// CAN modes +#define CAN_MODE_NORMAL 0U +#define CAN_MODE_GMLAN_CAN2 1U +#define CAN_MODE_GMLAN_CAN3 2U +#define CAN_MODE_OBD_CAN2 3U + +// Harness states +#define HARNESS_ORIENTATION_NONE 0U +#define HARNESS_ORIENTATION_1 1U +#define HARNESS_ORIENTATION_2 2U + +#define SBU1 0U +#define SBU2 1U + +// ********************* Globals ********************** +uint8_t harness_orientation = HARNESS_ORIENTATION_NONE; +uint8_t can_mode = CAN_MODE_NORMAL; +uint8_t ignition = 0U; + + +void unused_set_individual_ignition(uint8_t bitmask) { + UNUSED(bitmask); +} + +void unused_board_enable_header_pin(uint8_t pin_num, bool enabled) { + UNUSED(pin_num); + UNUSED(enabled); +} + +void unused_set_panda_individual_power(uint8_t port_num, bool enabled) { + UNUSED(port_num); + UNUSED(enabled); +} diff --git a/panda/board/jungle/boards/board_v1.h b/panda/board/jungle/boards/board_v1.h new file mode 100644 index 0000000..9581686 --- /dev/null +++ b/panda/board/jungle/boards/board_v1.h @@ -0,0 +1,178 @@ +// ///////////////////////// // +// Jungle board v1 (STM32F4) // +// ///////////////////////// // + +void board_v1_set_led(uint8_t color, bool enabled) { + switch (color) { + case LED_RED: + set_gpio_output(GPIOC, 9, !enabled); + break; + case LED_GREEN: + set_gpio_output(GPIOC, 7, !enabled); + break; + case LED_BLUE: + set_gpio_output(GPIOC, 6, !enabled); + break; + default: + break; + } +} + +void board_v1_enable_can_transciever(uint8_t transciever, bool enabled) { + switch (transciever) { + case 1U: + set_gpio_output(GPIOC, 1, !enabled); + break; + case 2U: + set_gpio_output(GPIOC, 13, !enabled); + break; + case 3U: + set_gpio_output(GPIOA, 0, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 10, !enabled); + break; + default: + print("Invalid CAN transciever ("); puth(transciever); print("): enabling failed\n"); + break; + } +} + +void board_v1_set_can_mode(uint8_t mode) { + board_v1_enable_can_transciever(2U, false); + board_v1_enable_can_transciever(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + print("Setting normal CAN mode\n"); + // B12,B13: disable OBD mode + set_gpio_mode(GPIOB, 12, MODE_INPUT); + set_gpio_mode(GPIOB, 13, MODE_INPUT); + + // B5,B6: normal CAN2 mode + set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + can_mode = CAN_MODE_NORMAL; + board_v1_enable_can_transciever(2U, true); + break; + case CAN_MODE_OBD_CAN2: + print("Setting OBD CAN mode\n"); + // B5,B6: disable normal CAN2 mode + set_gpio_mode(GPIOB, 5, MODE_INPUT); + set_gpio_mode(GPIOB, 6, MODE_INPUT); + + // B12,B13: OBD mode + set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + can_mode = CAN_MODE_OBD_CAN2; + board_v1_enable_can_transciever(4U, true); + break; + default: + print("Tried to set unsupported CAN mode: "); puth(mode); print("\n"); + break; + } +} + +void board_v1_set_harness_orientation(uint8_t orientation) { + switch (orientation) { + case HARNESS_ORIENTATION_NONE: + set_gpio_output(GPIOA, 2, false); + set_gpio_output(GPIOA, 3, false); + set_gpio_output(GPIOA, 4, false); + set_gpio_output(GPIOA, 5, false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_1: + set_gpio_output(GPIOA, 2, false); + set_gpio_output(GPIOA, 3, (ignition != 0U)); + set_gpio_output(GPIOA, 4, true); + set_gpio_output(GPIOA, 5, false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_2: + set_gpio_output(GPIOA, 2, (ignition != 0U)); + set_gpio_output(GPIOA, 3, false); + set_gpio_output(GPIOA, 4, false); + set_gpio_output(GPIOA, 5, true); + harness_orientation = orientation; + break; + default: + print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n"); + break; + } +} + +bool panda_power = false; +void board_v1_set_panda_power(bool enable) { + panda_power = enable; + set_gpio_output(GPIOB, 14, enable); +} + +bool board_v1_get_button(void) { + return get_gpio_input(GPIOC, 8); +} + +void board_v1_set_ignition(bool enabled) { + ignition = enabled ? 0xFFU : 0U; + board_v1_set_harness_orientation(harness_orientation); +} + +float board_v1_get_channel_power(uint8_t channel) { + UNUSED(channel); + return 0.0f; +} + +uint16_t board_v1_get_sbu_mV(uint8_t channel, uint8_t sbu) { + UNUSED(channel); UNUSED(sbu); + return 0U; +} + +void board_v1_init(void) { + common_init_gpio(); + + // A8,A15: normal CAN3 mode + set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3); + set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3); + + board_v1_set_can_mode(CAN_MODE_NORMAL); + + // Enable CAN transcievers + for(uint8_t i = 1; i <= 4; i++) { + board_v1_enable_can_transciever(i, true); + } + + // Disable LEDs + board_v1_set_led(LED_RED, false); + board_v1_set_led(LED_GREEN, false); + board_v1_set_led(LED_BLUE, false); + + // Set normal CAN mode + board_v1_set_can_mode(CAN_MODE_NORMAL); + + // Set to no harness orientation + board_v1_set_harness_orientation(HARNESS_ORIENTATION_NONE); + + // Enable panda power by default + board_v1_set_panda_power(true); +} + +void board_v1_tick(void) {} + +const board board_v1 = { + .has_canfd = false, + .has_sbu_sense = false, + .avdd_mV = 3300U, + .init = &board_v1_init, + .set_led = &board_v1_set_led, + .board_tick = &board_v1_tick, + .get_button = &board_v1_get_button, + .set_panda_power = &board_v1_set_panda_power, + .set_panda_individual_power = &unused_set_panda_individual_power, + .set_ignition = &board_v1_set_ignition, + .set_individual_ignition = &unused_set_individual_ignition, + .set_harness_orientation = &board_v1_set_harness_orientation, + .set_can_mode = &board_v1_set_can_mode, + .enable_can_transciever = &board_v1_enable_can_transciever, + .enable_header_pin = &unused_board_enable_header_pin, + .get_channel_power = &board_v1_get_channel_power, + .get_sbu_mV = &board_v1_get_sbu_mV, +}; diff --git a/panda/board/jungle/boards/board_v2.h b/panda/board/jungle/boards/board_v2.h new file mode 100644 index 0000000..0951148 --- /dev/null +++ b/panda/board/jungle/boards/board_v2.h @@ -0,0 +1,328 @@ +// ///////////////////////// // +// Jungle board v2 (STM32H7) // +// ///////////////////////// // + +const gpio_t power_pins[] = { + {.bank = GPIOA, .pin = 0}, + {.bank = GPIOA, .pin = 1}, + {.bank = GPIOF, .pin = 12}, + {.bank = GPIOA, .pin = 5}, + {.bank = GPIOC, .pin = 5}, + {.bank = GPIOB, .pin = 2}, +}; + +const gpio_t sbu1_ignition_pins[] = { + {.bank = GPIOD, .pin = 0}, + {.bank = GPIOD, .pin = 5}, + {.bank = GPIOD, .pin = 12}, + {.bank = GPIOD, .pin = 14}, + {.bank = GPIOE, .pin = 5}, + {.bank = GPIOE, .pin = 9}, +}; + +const gpio_t sbu1_relay_pins[] = { + {.bank = GPIOD, .pin = 1}, + {.bank = GPIOD, .pin = 6}, + {.bank = GPIOD, .pin = 11}, + {.bank = GPIOD, .pin = 15}, + {.bank = GPIOE, .pin = 6}, + {.bank = GPIOE, .pin = 10}, +}; + +const gpio_t sbu2_ignition_pins[] = { + {.bank = GPIOD, .pin = 3}, + {.bank = GPIOD, .pin = 8}, + {.bank = GPIOD, .pin = 9}, + {.bank = GPIOE, .pin = 0}, + {.bank = GPIOE, .pin = 7}, + {.bank = GPIOE, .pin = 11}, +}; + +const gpio_t sbu2_relay_pins[] = { + {.bank = GPIOD, .pin = 4}, + {.bank = GPIOD, .pin = 10}, + {.bank = GPIOD, .pin = 13}, + {.bank = GPIOE, .pin = 1}, + {.bank = GPIOE, .pin = 8}, + {.bank = GPIOE, .pin = 12}, +}; + +const adc_channel_t sbu1_channels[] = { + {.adc = ADC3, .channel = 12}, + {.adc = ADC3, .channel = 2}, + {.adc = ADC3, .channel = 4}, + {.adc = ADC3, .channel = 6}, + {.adc = ADC3, .channel = 8}, + {.adc = ADC3, .channel = 10}, +}; + +const adc_channel_t sbu2_channels[] = { + {.adc = ADC1, .channel = 13}, + {.adc = ADC3, .channel = 3}, + {.adc = ADC3, .channel = 5}, + {.adc = ADC3, .channel = 7}, + {.adc = ADC3, .channel = 9}, + {.adc = ADC3, .channel = 11}, +}; + +void board_v2_set_led(uint8_t color, bool enabled) { + switch (color) { + case LED_RED: + set_gpio_output(GPIOE, 4, !enabled); + break; + case LED_GREEN: + set_gpio_output(GPIOE, 3, !enabled); + break; + case LED_BLUE: + set_gpio_output(GPIOE, 2, !enabled); + break; + default: + break; + } +} + +void board_v2_set_harness_orientation(uint8_t orientation) { + switch (orientation) { + case HARNESS_ORIENTATION_NONE: + gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_1: + gpio_set_all_output(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), true); + gpio_set_bitmask(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), ignition); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), false); + harness_orientation = orientation; + break; + case HARNESS_ORIENTATION_2: + gpio_set_bitmask(sbu1_ignition_pins, sizeof(sbu1_ignition_pins) / sizeof(gpio_t), ignition); + gpio_set_all_output(sbu1_relay_pins, sizeof(sbu1_relay_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_ignition_pins, sizeof(sbu2_ignition_pins) / sizeof(gpio_t), false); + gpio_set_all_output(sbu2_relay_pins, sizeof(sbu2_relay_pins) / sizeof(gpio_t), true); + harness_orientation = orientation; + break; + default: + print("Tried to set an unsupported harness orientation: "); puth(orientation); print("\n"); + break; + } +} + +void board_v2_enable_can_transciever(uint8_t transciever, bool enabled) { + switch (transciever) { + case 1U: + set_gpio_output(GPIOG, 11, !enabled); + break; + case 2U: + set_gpio_output(GPIOB, 3, !enabled); + break; + case 3U: + set_gpio_output(GPIOD, 7, !enabled); + break; + case 4U: + set_gpio_output(GPIOB, 4, !enabled); + break; + default: + print("Invalid CAN transciever ("); puth(transciever); print("): enabling failed\n"); + break; + } +} + +void board_v2_enable_header_pin(uint8_t pin_num, bool enabled) { + if (pin_num < 8U) { + set_gpio_output(GPIOG, pin_num, enabled); + } else { + print("Invalid pin number ("); puth(pin_num); print("): enabling failed\n"); + } +} + +void board_v2_set_can_mode(uint8_t mode) { + board_v2_enable_can_transciever(2U, false); + board_v2_enable_can_transciever(4U, false); + switch (mode) { + case CAN_MODE_NORMAL: + // B12,B13: disable normal mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_mode(GPIOB, 12, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_mode(GPIOB, 13, MODE_ANALOG); + + // B5,B6: FDCAN2 mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + can_mode = CAN_MODE_NORMAL; + board_v2_enable_can_transciever(2U, true); + break; + case CAN_MODE_OBD_CAN2: + // B5,B6: disable normal mode + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_mode(GPIOB, 5, MODE_ANALOG); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_mode(GPIOB, 6, MODE_ANALOG); + // B12,B13: FDCAN2 mode + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_alternate(GPIOB, 12, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 13, PULL_NONE); + set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); + can_mode = CAN_MODE_OBD_CAN2; + board_v2_enable_can_transciever(4U, true); + break; + default: + break; + } +} + +bool panda_power = false; +uint8_t panda_power_bitmask = 0U; +void board_v2_set_panda_power(bool enable) { + panda_power = enable; + gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable); + if (enable) { + panda_power_bitmask = 0xFFU; + } else { + panda_power_bitmask = 0U; + } +} + +void board_v2_set_panda_individual_power(uint8_t port_num, bool enable) { + port_num -= 1U; + if (port_num < 6U) { + panda_power_bitmask &= ~(1U << port_num); + panda_power_bitmask |= (enable ? 1U : 0U) << port_num; + } else { + print("Invalid port number ("); puth(port_num); print("): enabling failed\n"); + } + gpio_set_bitmask(power_pins, sizeof(power_pins) / sizeof(gpio_t), (uint32_t)panda_power_bitmask); +} + +bool board_v2_get_button(void) { + return get_gpio_input(GPIOG, 15); +} + +void board_v2_set_ignition(bool enabled) { + ignition = enabled ? 0xFFU : 0U; + board_v2_set_harness_orientation(harness_orientation); +} + +void board_v2_set_individual_ignition(uint8_t bitmask) { + ignition = bitmask; + board_v2_set_harness_orientation(harness_orientation); +} + +float board_v2_get_channel_power(uint8_t channel) { + float ret = 0.0f; + if ((channel >= 1U) && (channel <= 6U)) { + uint16_t readout = adc_get_mV(ADC1, channel - 1U); // these are mapped nicely in hardware + + ret = (((float) readout / 33e6) - 0.8e-6) / 52e-6 * 12.0f; + } else { + print("Invalid channel ("); puth(channel); print(")\n"); + } + return ret; +} + +uint16_t board_v2_get_sbu_mV(uint8_t channel, uint8_t sbu) { + uint16_t ret = 0U; + if ((channel >= 1U) && (channel <= 6U)) { + switch(sbu){ + case SBU1: + ret = adc_get_mV(sbu1_channels[channel - 1U].adc, sbu1_channels[channel - 1U].channel); + break; + case SBU2: + ret = adc_get_mV(sbu2_channels[channel - 1U].adc, sbu2_channels[channel - 1U].channel); + break; + default: + print("Invalid SBU ("); puth(sbu); print(")\n"); + break; + } + } else { + print("Invalid channel ("); puth(channel); print(")\n"); + } + return ret; +} + +void board_v2_init(void) { + common_init_gpio(); + + // Disable LEDs + board_v2_set_led(LED_RED, false); + board_v2_set_led(LED_GREEN, false); + board_v2_set_led(LED_BLUE, false); + + // Normal CAN mode + board_v2_set_can_mode(CAN_MODE_NORMAL); + + // Enable CAN transcievers + for(uint8_t i = 1; i <= 4; i++) { + board_v2_enable_can_transciever(i, true); + } + + // Set to no harness orientation + board_v2_set_harness_orientation(HARNESS_ORIENTATION_NONE); + + // Enable panda power by default + board_v2_set_panda_power(true); + + // Current monitor channels + adc_init(ADC1); + register_set_bits(&SYSCFG->PMCR, SYSCFG_PMCR_PA0SO | SYSCFG_PMCR_PA1SO); // open up analog switches for PA0_C and PA1_C + set_gpio_mode(GPIOF, 11, MODE_ANALOG); + set_gpio_mode(GPIOA, 6, MODE_ANALOG); + set_gpio_mode(GPIOC, 4, MODE_ANALOG); + set_gpio_mode(GPIOB, 1, MODE_ANALOG); + + // SBU channels + adc_init(ADC3); + set_gpio_mode(GPIOC, 2, MODE_ANALOG); + set_gpio_mode(GPIOC, 3, MODE_ANALOG); + set_gpio_mode(GPIOF, 9, MODE_ANALOG); + set_gpio_mode(GPIOF, 7, MODE_ANALOG); + set_gpio_mode(GPIOF, 5, MODE_ANALOG); + set_gpio_mode(GPIOF, 3, MODE_ANALOG); + set_gpio_mode(GPIOF, 10, MODE_ANALOG); + set_gpio_mode(GPIOF, 8, MODE_ANALOG); + set_gpio_mode(GPIOF, 6, MODE_ANALOG); + set_gpio_mode(GPIOF, 4, MODE_ANALOG); + set_gpio_mode(GPIOC, 0, MODE_ANALOG); + set_gpio_mode(GPIOC, 1, MODE_ANALOG); + + // Header pins + set_gpio_mode(GPIOG, 0, MODE_OUTPUT); + set_gpio_mode(GPIOG, 1, MODE_OUTPUT); + set_gpio_mode(GPIOG, 2, MODE_OUTPUT); + set_gpio_mode(GPIOG, 3, MODE_OUTPUT); + set_gpio_mode(GPIOG, 4, MODE_OUTPUT); + set_gpio_mode(GPIOG, 5, MODE_OUTPUT); + set_gpio_mode(GPIOG, 6, MODE_OUTPUT); + set_gpio_mode(GPIOG, 7, MODE_OUTPUT); +} + +void board_v2_tick(void) {} + +const board board_v2 = { + .has_canfd = true, + .has_sbu_sense = true, + .avdd_mV = 3300U, + .init = &board_v2_init, + .set_led = &board_v2_set_led, + .board_tick = &board_v2_tick, + .get_button = &board_v2_get_button, + .set_panda_power = &board_v2_set_panda_power, + .set_panda_individual_power = &board_v2_set_panda_individual_power, + .set_ignition = &board_v2_set_ignition, + .set_individual_ignition = &board_v2_set_individual_ignition, + .set_harness_orientation = &board_v2_set_harness_orientation, + .set_can_mode = &board_v2_set_can_mode, + .enable_can_transciever = &board_v2_enable_can_transciever, + .enable_header_pin = &board_v2_enable_header_pin, + .get_channel_power = &board_v2_get_channel_power, + .get_sbu_mV = &board_v2_get_sbu_mV, +}; diff --git a/panda/board/jungle/jungle_health.h b/panda/board/jungle/jungle_health.h new file mode 100644 index 0000000..931ed37 --- /dev/null +++ b/panda/board/jungle/jungle_health.h @@ -0,0 +1,24 @@ +// When changing these structs, python/__init__.py needs to be kept up to date! + +#define JUNGLE_HEALTH_PACKET_VERSION 1 +struct __attribute__((packed)) jungle_health_t { + uint32_t uptime_pkt; + float ch1_power; + float ch2_power; + float ch3_power; + float ch4_power; + float ch5_power; + float ch6_power; + uint16_t ch1_sbu1_mV; + uint16_t ch1_sbu2_mV; + uint16_t ch2_sbu1_mV; + uint16_t ch2_sbu2_mV; + uint16_t ch3_sbu1_mV; + uint16_t ch3_sbu2_mV; + uint16_t ch4_sbu1_mV; + uint16_t ch4_sbu2_mV; + uint16_t ch5_sbu1_mV; + uint16_t ch5_sbu2_mV; + uint16_t ch6_sbu1_mV; + uint16_t ch6_sbu2_mV; +}; diff --git a/panda/board/jungle/main_comms.h b/panda/board/jungle/main_comms.h new file mode 100644 index 0000000..928c554 --- /dev/null +++ b/panda/board/jungle/main_comms.h @@ -0,0 +1,261 @@ +extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used + +int get_jungle_health_pkt(void *dat) { + COMPILE_TIME_ASSERT(sizeof(struct jungle_health_t) <= USBPACKET_MAX_SIZE); + struct jungle_health_t * health = (struct jungle_health_t*)dat; + + health->uptime_pkt = uptime_cnt; + health->ch1_power = current_board->get_channel_power(1U); + health->ch2_power = current_board->get_channel_power(2U); + health->ch3_power = current_board->get_channel_power(3U); + health->ch4_power = current_board->get_channel_power(4U); + health->ch5_power = current_board->get_channel_power(5U); + health->ch6_power = current_board->get_channel_power(6U); + + health->ch1_sbu1_mV = current_board->get_sbu_mV(1U, SBU1); + health->ch1_sbu2_mV = current_board->get_sbu_mV(1U, SBU2); + health->ch2_sbu1_mV = current_board->get_sbu_mV(2U, SBU1); + health->ch2_sbu2_mV = current_board->get_sbu_mV(2U, SBU2); + health->ch3_sbu1_mV = current_board->get_sbu_mV(3U, SBU1); + health->ch3_sbu2_mV = current_board->get_sbu_mV(3U, SBU2); + health->ch4_sbu1_mV = current_board->get_sbu_mV(4U, SBU1); + health->ch4_sbu2_mV = current_board->get_sbu_mV(4U, SBU2); + health->ch5_sbu1_mV = current_board->get_sbu_mV(5U, SBU1); + health->ch5_sbu2_mV = current_board->get_sbu_mV(5U, SBU2); + health->ch6_sbu1_mV = current_board->get_sbu_mV(6U, SBU1); + health->ch6_sbu2_mV = current_board->get_sbu_mV(6U, SBU2); + + return sizeof(*health); +} + +// send on serial, first byte to select the ring +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + UNUSED(data); + UNUSED(len); +} + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + unsigned int resp_len = 0; + uint32_t time; + +#ifdef DEBUG_COMMS + print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); + print("- request "); puth(req->request); print("\n"); + print("- param1 "); puth(req->param1); print("\n"); + print("- param2 "); puth(req->param2); print("\n"); +#endif + + switch (req->request) { + // **** 0xa0: Set panda power. + case 0xa0: + current_board->set_panda_power((req->param1 == 1U)); + break; + // **** 0xa1: Set harness orientation. + case 0xa1: + current_board->set_harness_orientation(req->param1); + break; + // **** 0xa2: Set ignition. + case 0xa2: + current_board->set_ignition((req->param1 == 1U)); + break; + // **** 0xa0: Set panda power per channel by bitmask. + case 0xa3: + current_board->set_panda_individual_power(req->param1, (req->param2 > 0U)); + break; + // **** 0xa8: get microsecond timer + case 0xa8: + time = microsecond_timer_get(); + resp[0] = (time & 0x000000FFU); + resp[1] = ((time & 0x0000FF00U) >> 8U); + resp[2] = ((time & 0x00FF0000U) >> 16U); + resp[3] = ((time & 0xFF000000U) >> 24U); + resp_len = 4U; + break; + // **** 0xc0: reset communications + case 0xc0: + comms_can_reset(); + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc2: CAN health stats + case 0xc2: + COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); + if (req->param1 < 3U) { + update_can_health_pkt(req->param1, 0U); + can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); + can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); + can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; + can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; + can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; + resp_len = sizeof(can_health[req->param1]); + (void)memcpy(resp, &can_health[req->param1], resp_len); + } + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xd0: fetch serial (aka the provisioned dongle ID) + case 0xd0: + // addresses are OTP + if (req->param1 == 1U) { + (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + // only allow bootloader entry on debug builds + #ifdef ALLOW_DEBUG + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + #endif + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + default: + print("Bootloader mode invalid\n"); + break; + } + break; + // **** 0xd2: get health packet + case 0xd2: + resp_len = get_jungle_health_pkt(resp); + break; + // **** 0xd3: get first 64 bytes of signature + case 0xd3: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len], resp_len); + } + break; + // **** 0xd4: get second 64 bytes of signature + case 0xd4: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len + 64], resp_len); + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + (void)memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion) - 1U; + break; + // **** 0xd8: reset ST + case 0xd8: + NVIC_SystemReset(); + break; + // **** 0xdb: set OBD CAN multiplexing mode + case 0xdb: + if (req->param1 == 1U) { + // Enable OBD CAN + current_board->set_can_mode(CAN_MODE_OBD_CAN2); + } else { + // Disable OBD CAN + current_board->set_can_mode(CAN_MODE_NORMAL); + } + break; + // **** 0xdd: get healthpacket and CANPacket versions + case 0xdd: + resp[0] = JUNGLE_HEALTH_PACKET_VERSION; + resp[1] = CAN_PACKET_VERSION; + resp[2] = CAN_HEALTH_PACKET_VERSION; + resp_len = 3; + break; + // **** 0xde: set can bitrate + case 0xde: + if ((req->param1 < PANDA_BUS_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { + bus_config[req->param1].can_speed = req->param2; + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xe0: debug read + case 0xe0: + // read + while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && get_char(get_ring_by_number(0), (char*)&resp[resp_len])) { + ++resp_len; + } + break; + // **** 0xe5: set CAN loopback (for testing) + case 0xe5: + can_loopback = (req->param1 > 0U); + can_init_all(); + break; + // **** 0xf1: Clear CAN ring buffer. + case 0xf1: + if (req->param1 == 0xFFFFU) { + print("Clearing CAN Rx queue\n"); + can_clear(&can_rx_q); + } else if (req->param1 < PANDA_BUS_CNT) { + print("Clearing CAN Tx queue\n"); + can_clear(can_queues[req->param1]); + } else { + print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); + } + break; + // **** 0xf2: Clear debug ring buffer. + case 0xf2: + print("Clearing debug queue.\n"); + clear_uart_buff(get_ring_by_number(0)); + break; + // **** 0xf4: Set CAN transceiver enable pin + case 0xf4: + current_board->enable_can_transciever(req->param1, req->param2 > 0U); + break; + // **** 0xf5: Set CAN silent mode + case 0xf5: + can_silent = (req->param1 > 0U) ? ALL_CAN_SILENT : ALL_CAN_LIVE; + can_init_all(); + break; + // **** 0xf7: enable/disable header pin by number + case 0xf7: + current_board->enable_header_pin(req->param1, req->param2 > 0U); + break; + // **** 0xf9: set CAN FD data bitrate + case 0xf9: + if ((req->param1 < PANDA_CAN_CNT) && + current_board->has_canfd && + is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { + bus_config[req->param1].can_data_speed = req->param2; + bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); + bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xfc: set CAN FD non-ISO mode + case 0xfc: + if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd) { + bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + default: + print("NO HANDLER "); + puth(req->request); + print("\n"); + break; + } + return resp_len; +} diff --git a/panda/board/jungle/obj/bootstub.panda_jungle.bin b/panda/board/jungle/obj/bootstub.panda_jungle.bin deleted file mode 100755 index 221d1cfbd391582eade1edccfe915fbf91df8dcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11072 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 JN+>uX{{@530uulL diff --git a/panda/board/jungle/obj/bootstub.panda_jungle.elf b/panda/board/jungle/obj/bootstub.panda_jungle.elf deleted file mode 100755 index 028138a7454bdb634d19dd5db197e34de12a4191..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 220372 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

g!tUtP)!R#%1yK+rQY#LL!Hluoa$bMSjeQ{K!mR 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 diff --git a/panda/board/jungle/obj/panda_jungle_h7.bin b/panda/board/jungle/obj/panda_jungle_h7.bin deleted file mode 100755 index 294d67dc9bf26e62e6f9712a3bf1375ee891889b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64148 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'PF<2(`X7r$*0e? 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

>JeB>&LUsI#yL)pHN7~ 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%0D)>+4qoKe$|0v)C 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 MRNu-G{m0w=57fAJt^fc4 diff --git a/panda/board/jungle/scripts/get_version.py b/panda/board/jungle/scripts/get_version.py index ad4a1c4..4fc9d30 100755 --- a/panda/board/jungle/scripts/get_version.py +++ b/panda/board/jungle/scripts/get_version.py @@ -4,6 +4,6 @@ from panda import PandaJungle if __name__ == "__main__": for p in PandaJungle.list(): pp = PandaJungle(p) - print("%s: %s" % (pp.get_serial()[0], pp.get_version())) + print(f"{pp.get_serial()[0]}: {pp.get_version()}") diff --git a/panda/board/jungle/stm32f4/board.h b/panda/board/jungle/stm32f4/board.h new file mode 100644 index 0000000..0adf792 --- /dev/null +++ b/panda/board/jungle/stm32f4/board.h @@ -0,0 +1,7 @@ +#include "boards/board_declarations.h" +#include "boards/board_v1.h" + +void detect_board_type(void) { + hw_type = HW_TYPE_V1; + current_board = &board_v1; +} diff --git a/panda/board/jungle/stm32h7/board.h b/panda/board/jungle/stm32h7/board.h new file mode 100644 index 0000000..4fe4fa4 --- /dev/null +++ b/panda/board/jungle/stm32h7/board.h @@ -0,0 +1,9 @@ +#include "boards/board_declarations.h" + +#include "stm32h7/lladc.h" +#include "boards/board_v2.h" + +void detect_board_type(void) { + hw_type = HW_TYPE_V2; + current_board = &board_v2; +} diff --git a/panda/board/jungle/stm32h7/lladc.h b/panda/board/jungle/stm32h7/lladc.h new file mode 100644 index 0000000..06b742d --- /dev/null +++ b/panda/board/jungle/stm32h7/lladc.h @@ -0,0 +1,54 @@ + +typedef struct { + ADC_TypeDef *adc; + uint8_t channel; +} adc_channel_t; + +void adc_init(ADC_TypeDef *adc) { + adc->CR &= ~(ADC_CR_DEEPPWD); // Reset deep-power-down mode + adc->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator + while(!(adc->ISR & ADC_ISR_LDORDY) && (adc != ADC3)); + + if (adc != ADC3) { + adc->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration + adc->CR |= ADC_CR_ADCALLIN; // Lineriality calibration + } + adc->CR |= ADC_CR_ADCAL; // Start calibrtation + while((adc->CR & ADC_CR_ADCAL) != 0); + + adc->ISR |= ADC_ISR_ADRDY; + adc->CR |= ADC_CR_ADEN; + while(!(adc->ISR & ADC_ISR_ADRDY)); +} + +uint16_t adc_get_raw(ADC_TypeDef *adc, uint8_t channel) { + adc->SQR1 &= ~(ADC_SQR1_L); + adc->SQR1 = ((uint32_t) channel << 6U); + + if (channel < 10U) { + adc->SMPR1 = (0x7U << (channel * 3U)); + } else { + adc->SMPR2 = (0x7U << ((channel - 10U) * 3U)); + } + adc->PCSEL_RES0 = (0x1U << channel); + + adc->CR |= ADC_CR_ADSTART; + while (!(adc->ISR & ADC_ISR_EOC)); + + uint16_t res = adc->DR; + + while (!(adc->ISR & ADC_ISR_EOS)); + adc->ISR |= ADC_ISR_EOS; + + return res; +} + +uint16_t adc_get_mV(ADC_TypeDef *adc, uint8_t channel) { + uint16_t ret = 0; + if ((adc == ADC1) || (adc == ADC2)) { + ret = (adc_get_raw(adc, channel) * current_board->avdd_mV) / 65535U; + } else if (adc == ADC3) { + ret = (adc_get_raw(adc, channel) * current_board->avdd_mV) / 4095U; + } else {} + return ret; +} diff --git a/panda/board/libc.h b/panda/board/libc.h new file mode 100644 index 0000000..7f669b9 --- /dev/null +++ b/panda/board/libc.h @@ -0,0 +1,67 @@ +// **** libc **** + +void delay(uint32_t a) { + volatile uint32_t i; + for (i = 0; i < a; i++); +} + +// cppcheck-suppress misra-c2012-21.2 +void *memset(void *str, int c, unsigned int n) { + uint8_t *s = str; + for (unsigned int i = 0; i < n; i++) { + *s = c; + s++; + } + return str; +} + +#define UNALIGNED(X, Y) \ + (((uint32_t)(X) & (sizeof(uint32_t) - 1U)) | ((uint32_t)(Y) & (sizeof(uint32_t) - 1U))) + +// cppcheck-suppress misra-c2012-21.2 +void *memcpy(void *dest, const void *src, unsigned int len) { + unsigned int n = len; + uint8_t *d8 = dest; + const uint8_t *s8 = src; + + if ((n >= 4U) && !UNALIGNED(s8, d8)) { + uint32_t *d32 = (uint32_t *)d8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + const uint32_t *s32 = (const uint32_t *)s8; // cppcheck-suppress misra-c2012-11.3 ; already checked that it's properly aligned + + while(n >= 16U) { + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + *d32 = *s32; d32++; s32++; + n -= 16U; + } + + while(n >= 4U) { + *d32 = *s32; d32++; s32++; + n -= 4U; + } + + d8 = (uint8_t *)d32; + s8 = (const uint8_t *)s32; + } + while (n-- > 0U) { + *d8 = *s8; d8++; s8++; + } + return dest; +} + +// cppcheck-suppress misra-c2012-21.2 +int memcmp(const void * ptr1, const void * ptr2, unsigned int num) { + int ret = 0; + const uint8_t *p1 = ptr1; + const uint8_t *p2 = ptr2; + for (unsigned int i = 0; i < num; i++) { + if (*p1 != *p2) { + ret = -1; + break; + } + p1++; + p2++; + } + return ret; +} diff --git a/panda/board/main.c b/panda/board/main.c index 061a913..00429e3 100644 --- a/panda/board/main.c +++ b/panda/board/main.c @@ -294,24 +294,6 @@ void EXTI_IRQ_Handler(void) { } } -uint8_t rtc_counter = 0; -void RTC_WKUP_IRQ_Handler(void) { - exti_irq_clear(); - clock_init(); - - rtc_counter++; - if ((rtc_counter % 2U) == 0U) { - current_board->set_led(LED_BLUE, false); - } else { - current_board->set_led(LED_BLUE, true); - } - - if (rtc_counter == __UINT8_MAX__) { - rtc_counter = 1U; - } -} - - int main(void) { // Init interrupt table init_interrupts(true); @@ -422,10 +404,6 @@ int main(void) { // Init IRQs for CAN transceiver and ignition line exti_irq_init(); - // Init RTC Wakeup event on EXTI22 - REGISTER_INTERRUPT(RTC_WKUP_IRQn, RTC_WKUP_IRQ_Handler, 10U, FAULT_INTERRUPT_RATE_EXTI) - rtc_wakeup_init(); - // STOP mode SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; } diff --git a/panda/board/main_comms.h b/panda/board/main_comms.h new file mode 100644 index 0000000..2c8bf16 --- /dev/null +++ b/panda/board/main_comms.h @@ -0,0 +1,397 @@ +extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used + +// Prototypes +void set_safety_mode(uint16_t mode, uint16_t param); +bool is_car_safety_mode(uint16_t mode); + +int get_health_pkt(void *dat) { + COMPILE_TIME_ASSERT(sizeof(struct health_t) <= USBPACKET_MAX_SIZE); + struct health_t * health = (struct health_t*)dat; + + health->uptime_pkt = uptime_cnt; + health->voltage_pkt = current_board->read_voltage_mV(); + health->current_pkt = current_board->read_current_mA(); + + // Use the GPIO pin to determine ignition or use a CAN based logic + health->ignition_line_pkt = (uint8_t)(current_board->check_ignition()); + health->ignition_can_pkt = ignition_can; + + health->controls_allowed_pkt = controls_allowed; + health->safety_tx_blocked_pkt = safety_tx_blocked; + health->safety_rx_invalid_pkt = safety_rx_invalid; + health->tx_buffer_overflow_pkt = tx_buffer_overflow; + health->rx_buffer_overflow_pkt = rx_buffer_overflow; + health->gmlan_send_errs_pkt = gmlan_send_errs; + health->car_harness_status_pkt = harness.status; + health->safety_mode_pkt = (uint8_t)(current_safety_mode); + health->safety_param_pkt = current_safety_param; + health->alternative_experience_pkt = alternative_experience; + health->power_save_enabled_pkt = power_save_status == POWER_SAVE_STATUS_ENABLED; + health->heartbeat_lost_pkt = heartbeat_lost; + health->safety_rx_checks_invalid_pkt = safety_rx_checks_invalid; + + health->spi_checksum_error_count_pkt = spi_checksum_error_count; + + health->fault_status_pkt = fault_status; + health->faults_pkt = faults; + + health->interrupt_load_pkt = interrupt_load; + + health->fan_power = fan_state.power; + health->fan_stall_count = fan_state.total_stall_count; + + health->sbu1_voltage_mV = harness.sbu1_voltage_mV; + health->sbu2_voltage_mV = harness.sbu2_voltage_mV; + + health->som_reset_triggered = bootkick_reset_triggered; + + return sizeof(*health); +} + +// send on serial, first byte to select the ring +void comms_endpoint2_write(const uint8_t *data, uint32_t len) { + uart_ring *ur = get_ring_by_number(data[0]); + if ((len != 0U) && (ur != NULL)) { + if ((data[0] < 2U) || (data[0] >= 4U)) { + for (uint32_t i = 1; i < len; i++) { + while (!put_char(ur, data[i])) { + // wait + } + } + } + } +} + +int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { + unsigned int resp_len = 0; + uart_ring *ur = NULL; + uint32_t time; + +#ifdef DEBUG_COMMS + print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n"); + print("- request "); puth(req->request); print("\n"); + print("- param1 "); puth(req->param1); print("\n"); + print("- param2 "); puth(req->param2); print("\n"); +#endif + + switch (req->request) { + // **** 0xa8: get microsecond timer + case 0xa8: + time = microsecond_timer_get(); + resp[0] = (time & 0x000000FFU); + resp[1] = ((time & 0x0000FF00U) >> 8U); + resp[2] = ((time & 0x00FF0000U) >> 16U); + resp[3] = ((time & 0xFF000000U) >> 24U); + resp_len = 4U; + break; + // **** 0xb0: set IR power + case 0xb0: + current_board->set_ir_power(req->param1); + break; + // **** 0xb1: set fan power + case 0xb1: + fan_set_power(req->param1); + break; + // **** 0xb2: get fan rpm + case 0xb2: + resp[0] = (fan_state.rpm & 0x00FFU); + resp[1] = ((fan_state.rpm & 0xFF00U) >> 8U); + resp_len = 2; + break; + // **** 0xc0: reset communications + case 0xc0: + comms_can_reset(); + break; + // **** 0xc1: get hardware type + case 0xc1: + resp[0] = hw_type; + resp_len = 1; + break; + // **** 0xc2: CAN health stats + case 0xc2: + COMPILE_TIME_ASSERT(sizeof(can_health_t) <= USBPACKET_MAX_SIZE); + if (req->param1 < 3U) { + update_can_health_pkt(req->param1, 0U); + can_health[req->param1].can_speed = (bus_config[req->param1].can_speed / 10U); + can_health[req->param1].can_data_speed = (bus_config[req->param1].can_data_speed / 10U); + can_health[req->param1].canfd_enabled = bus_config[req->param1].canfd_enabled; + can_health[req->param1].brs_enabled = bus_config[req->param1].brs_enabled; + can_health[req->param1].canfd_non_iso = bus_config[req->param1].canfd_non_iso; + resp_len = sizeof(can_health[req->param1]); + (void)memcpy(resp, &can_health[req->param1], resp_len); + } + break; + // **** 0xc3: fetch MCU UID + case 0xc3: + (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); + resp_len = 12; + break; + // **** 0xc4: get interrupt call rate + case 0xc4: + if (req->param1 < NUM_INTERRUPTS) { + uint32_t load = interrupts[req->param1].call_rate; + resp[0] = (load & 0x000000FFU); + resp[1] = ((load & 0x0000FF00U) >> 8U); + resp[2] = ((load & 0x00FF0000U) >> 16U); + resp[3] = ((load & 0xFF000000U) >> 24U); + resp_len = 4U; + } + break; + // **** 0xc5: DEBUG: drive relay + case 0xc5: + set_intercept_relay((req->param1 & 0x1U), (req->param1 & 0x2U)); + break; + // **** 0xc6: DEBUG: read SOM GPIO + case 0xc6: + resp[0] = current_board->read_som_gpio(); + resp_len = 1; + break; + // **** 0xd0: fetch serial (aka the provisioned dongle ID) + case 0xd0: + // addresses are OTP + if (req->param1 == 1U) { + (void)memcpy(resp, (uint8_t *)DEVICE_SERIAL_NUMBER_ADDRESS, 0x10); + resp_len = 0x10; + } else { + get_provision_chunk(resp); + resp_len = PROVISION_CHUNK_LEN; + } + break; + // **** 0xd1: enter bootloader mode + case 0xd1: + // this allows reflashing of the bootstub + switch (req->param1) { + case 0: + // only allow bootloader entry on debug builds + #ifdef ALLOW_DEBUG + print("-> entering bootloader\n"); + enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; + NVIC_SystemReset(); + #endif + break; + case 1: + print("-> entering softloader\n"); + enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; + NVIC_SystemReset(); + break; + default: + print("Bootloader mode invalid\n"); + break; + } + break; + // **** 0xd2: get health packet + case 0xd2: + resp_len = get_health_pkt(resp); + break; + // **** 0xd3: get first 64 bytes of signature + case 0xd3: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len], resp_len); + } + break; + // **** 0xd4: get second 64 bytes of signature + case 0xd4: + { + resp_len = 64; + char * code = (char*)_app_start; + int code_len = _app_start[0]; + (void)memcpy(resp, &code[code_len + 64], resp_len); + } + break; + // **** 0xd6: get version + case 0xd6: + COMPILE_TIME_ASSERT(sizeof(gitversion) <= USBPACKET_MAX_SIZE); + (void)memcpy(resp, gitversion, sizeof(gitversion)); + resp_len = sizeof(gitversion) - 1U; + break; + // **** 0xd8: reset ST + case 0xd8: + NVIC_SystemReset(); + break; + // **** 0xdb: set GMLAN (white/grey) or OBD CAN (black) multiplexing mode + case 0xdb: + if(current_board->has_obd){ + if (req->param1 == 1U) { + // Enable OBD CAN + current_board->set_can_mode(CAN_MODE_OBD_CAN2); + } else { + // Disable OBD CAN + current_board->set_can_mode(CAN_MODE_NORMAL); + } + } + break; + + // **** 0xdc: set safety mode + case 0xdc: + set_safety_mode(req->param1, (uint16_t)req->param2); + break; + // **** 0xdd: get healthpacket and CANPacket versions + case 0xdd: + resp[0] = HEALTH_PACKET_VERSION; + resp[1] = CAN_PACKET_VERSION; + resp[2] = CAN_HEALTH_PACKET_VERSION; + resp_len = 3; + break; + // **** 0xde: set can bitrate + case 0xde: + if ((req->param1 < PANDA_BUS_CNT) && is_speed_valid(req->param2, speeds, sizeof(speeds)/sizeof(speeds[0]))) { + bus_config[req->param1].can_speed = req->param2; + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xdf: set alternative experience + case 0xdf: + // you can only set this if you are in a non car safety mode + if (!is_car_safety_mode(current_safety_mode)) { + alternative_experience = req->param1; + } + break; + // **** 0xe0: uart read + case 0xe0: + ur = get_ring_by_number(req->param1); + if (!ur) { + break; + } + + // read + uint16_t req_length = MIN(req->length, USBPACKET_MAX_SIZE); + while ((resp_len < req_length) && + get_char(ur, (char*)&resp[resp_len])) { + ++resp_len; + } + break; + // **** 0xe1: uart set baud rate + case 0xe1: + ur = get_ring_by_number(req->param1); + if (!ur) { + break; + } + uart_set_baud(ur->uart, req->param2); + break; + // **** 0xe2: uart set parity + case 0xe2: + ur = get_ring_by_number(req->param1); + if (!ur) { + break; + } + switch (req->param2) { + case 0: + // disable parity, 8-bit + ur->uart->CR1 &= ~(USART_CR1_PCE | USART_CR1_M); + break; + case 1: + // even parity, 9-bit + ur->uart->CR1 &= ~USART_CR1_PS; + ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; + break; + case 2: + // odd parity, 9-bit + ur->uart->CR1 |= USART_CR1_PS; + ur->uart->CR1 |= USART_CR1_PCE | USART_CR1_M; + break; + default: + break; + } + break; + // **** 0xe4: uart set baud rate extended + case 0xe4: + ur = get_ring_by_number(req->param1); + if (!ur) { + break; + } + uart_set_baud(ur->uart, (int)req->param2*300); + break; + // **** 0xe5: set CAN loopback (for testing) + case 0xe5: + can_loopback = req->param1 > 0U; + can_init_all(); + break; + // **** 0xe6: set custom clock source period + case 0xe6: + clock_source_set_period(req->param1); + break; + // **** 0xe7: set power save state + case 0xe7: + set_power_save_state(req->param1); + break; + // **** 0xf1: Clear CAN ring buffer. + case 0xf1: + if (req->param1 == 0xFFFFU) { + print("Clearing CAN Rx queue\n"); + can_clear(&can_rx_q); + } else if (req->param1 < PANDA_BUS_CNT) { + print("Clearing CAN Tx queue\n"); + can_clear(can_queues[req->param1]); + } else { + print("Clearing CAN CAN ring buffer failed: wrong bus number\n"); + } + break; + // **** 0xf2: Clear UART ring buffer. + case 0xf2: + { + uart_ring * rb = get_ring_by_number(req->param1); + if (rb != NULL) { + print("Clearing UART queue.\n"); + clear_uart_buff(rb); + } + break; + } + // **** 0xf3: Heartbeat. Resets heartbeat counter. + case 0xf3: + { + heartbeat_counter = 0U; + heartbeat_lost = false; + heartbeat_disabled = false; + heartbeat_engaged = (req->param1 == 1U); + break; + } + // **** 0xf6: set siren enabled + case 0xf6: + siren_enabled = (req->param1 != 0U); + break; + // **** 0xf7: set green led enabled + case 0xf7: + green_led_enabled = (req->param1 != 0U); + break; + // **** 0xf8: disable heartbeat checks + case 0xf8: + if (!is_car_safety_mode(current_safety_mode)) { + heartbeat_disabled = true; + } + break; + // **** 0xf9: set CAN FD data bitrate + case 0xf9: + if ((req->param1 < PANDA_CAN_CNT) && + current_board->has_canfd && + is_speed_valid(req->param2, data_speeds, sizeof(data_speeds)/sizeof(data_speeds[0]))) { + bus_config[req->param1].can_data_speed = req->param2; + bus_config[req->param1].canfd_enabled = (req->param2 >= bus_config[req->param1].can_speed); + bus_config[req->param1].brs_enabled = (req->param2 > bus_config[req->param1].can_speed); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + // **** 0xfb: allow highest power saving mode (stop) to be entered + case 0xfb: + deepsleep_allowed = true; + break; + // **** 0xfc: set CAN FD non-ISO mode + case 0xfc: + if ((req->param1 < PANDA_CAN_CNT) && current_board->has_canfd) { + bus_config[req->param1].canfd_non_iso = (req->param2 != 0U); + bool ret = can_init(CAN_NUM_FROM_BUS_NUM(req->param1)); + UNUSED(ret); + } + break; + default: + print("NO HANDLER "); + puth(req->request); + print("\n"); + break; + } + return resp_len; +} diff --git a/panda/board/main_declarations.h b/panda/board/main_declarations.h new file mode 100644 index 0000000..4570c6e --- /dev/null +++ b/panda/board/main_declarations.h @@ -0,0 +1,32 @@ +// ******************** Prototypes ******************** +void print(const char *a); +void puth(unsigned int i); +void puth2(unsigned int i); +void puth4(unsigned int i); +void hexdump(const void *a, int l); +typedef struct board board; +typedef struct harness_configuration harness_configuration; +void can_flip_buses(uint8_t bus1, uint8_t bus2); +void pwm_init(TIM_TypeDef *TIM, uint8_t channel); +void pwm_set(TIM_TypeDef *TIM, uint8_t channel, uint8_t percentage); + +// ********************* Globals ********************** +uint8_t hw_type = 0; +const board *current_board; +uint32_t uptime_cnt = 0; +bool green_led_enabled = false; + +// heartbeat state +uint32_t heartbeat_counter = 0; +bool heartbeat_lost = false; +bool heartbeat_disabled = false; // set over USB + +// Enter deep sleep mode +bool deepsleep_allowed = false; +bool ignition_seen = false; + +// siren state +bool siren_enabled = false; +uint32_t siren_countdown = 0; // siren plays while countdown > 0 +uint32_t controls_allowed_countdown = 0; + diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin deleted file mode 100755 index da63792f92243dc5e8c0adb657c9e6a29e1deca8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15716 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 Y)_qtGKg=ijKKGR-^;JzV{~X`{3;mO?f&c&j diff --git a/panda/board/obj/bootstub.panda_h7.bin b/panda/board/obj/bootstub.panda_h7.bin deleted file mode 100755 index 05ca5a6eb5dbfe414f9079a098807a2748f36cef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17580 zcmch83s_Xu`uEzGVTPLw7nPej7g1oe2C%}k+zzlA7!;8%s9gq-9JCZc%d~R5R9Kdk zcJgvEi|%HDhQ%16VW;~!5P6Q5E|zshC2df2Z-JTp{niG|`k(LlzW?|9pKo~Hwch=% z+q>3!*Sp^Jt_2~ip>Yz?F_Zr*kU#wf^HB029z2xjA^<}H2>>HtEMPn!127RV1&{-9 z0Ez&&0&WMC11bTF080V)4I{cWfCmBV04qRS1)yy`fp{Ze3*ZI7D}bGV2EcoOW@4eApYn0N*)ID{h4;< z_kSw?XWHQRmG%9ZHu(Mj5)amUW!e8kyE6Ssd;`Yn*(=5?TTti2;NnR?0vRRlR^PLmoa=VD;-OeF=1>&ioG8tEizYXv7jz8rqe7v3)H5-XJ z_!-s}D{i%Eza>fE$YlRpIYHls);FK@Z9oaaWOR9hw-x$G=n&UB@jE%@c(I&Bc8hn& z{G=1TWM{eSD>-&YNG~T7vvy;h>$_7y^G@{Y=J8!&9KTVsjq6;`eRYbRr$bBJ_OE18 zKT2da>bBM7=-U%IgGgAHGC8+X%TATYkzwL3r?nj0W)Kx=lfqo{o|m@r-6^s&UOXc6 z*7!~?gx}`qBB4K_LPiYUQju1K5t_$QrUNl$) z-`j3?s^Ht{eq>t=zFu>;$vTEh=GyAJm{Ef71@}VHF>+;Ex)?1pD<8M9GPB}Rdw7>S zEJGy5dE!R-K?9SvQGVb!KPiegq}WamDcdNoIgXg!D6gimsQ_)CZm0DXwio(zI=-ux zyIvIDp#2dQVW$&VJa9pA?xa+TU$4C1#przP&Nn8B!kaW_n&N!JB+is|lO{DVlQ5T6 zGcga3+PIF3M`*fMF}u=5W)iWmT|Ap{G-0QvD~5?*HdJOpZNd{aPG&4p`-psE9tKL3 z%p}y>u5M>C+?I>&j3FH3fw^79CC9X1$=}7m@BH}!0=RLi+?)pfo|hOKz2Jl}llUPR znJ`x73wVTF0UyH3Y{C0pVrci0mU`$hDqHRKq)X3{t{Iy7VvLBHN(lDsv}Gff+XZ8c z$V{ZXC(SQku;#Red&M={MdLN6d9LvE`I?UMGwp4sVqR}MHSAaILQ$M6E*1BQ?c(jS z(GZ=&c3vvqmTms_VdTyb$_1{gF^jt}GJ`v}C7MZ_lU;avFIy+hNSQ8%w-;wuXIErT z&gLp~ox$AdimZx{EBL#LROuckZV`uy-+0HSgnau5TBkLAhnn_q+=YK-88Z%hC#8gc zE1uS{K}a?_jBXP(MbgHCeZ0GH(5VuO5B zxV^Etoj=`&T=QPBk#@N`MrIZqh@E2Lx)^rI61H8D@)Tl<7Jq}ZONA&|fpr@4gZy*- z8N!&Z&Ev(K)3+?eOlU6>MQT^-T%Ilx!TF}>bS>|9Wk%f8ZW&?_8B=(dmU%+fS{129 zv4mau{hne8x$^rR_lLgj)6VhVOdicI=uS6yoBDOS&Q#d1=+#?g9;M6#IS_|mFel+hDG z9?->Axu6*Oq0m>KwYt7TuC;PLo?|vL+vaGewrKLBYnhB_H%;YDt#Yk_t*^>`9pza^ zY~}v*ZlczyV@p^^!g8*mY%l$$y1sg^#(}wY@_DnD*E$*FmTI=?S8r7=O(Q%j(6;~l zx4NE~Y0GPDOb2a;*~VmC^s?EUxDnD+l?y$QaLLQ1`WiJyrk*C6P>~mRY17++usYMT z|6-fa?rScV^>^!U$VON!huxhGD3*1Ib%=Sy{M`}b`7S-rVYQz;938yK7s2zroQZUD z!q8e@ZUY zh{MI|y(0^RbIP?p$V+w<`ocmt$-(Y+QP;(A<<+cf^=XEiBoqrAB>Gz=w3nQpEpyh# z8+O_v+NL+HMc((Z=Gb<#I-kuasGMO>kRU}Ll ziiEAM>$7G#w>p3D()pb+lo&!F&+oN_h@WLC(w>&8#>fIPCUTnDwWpza?}FuJE+%6^ zwb_;6Di&UHMccTx!frQuyG&WIoSAfN_k!x^CX3oWb1iz35jJ7F?Ra|?)&s3ul_9ex zA1T&2k;#a3KJ5%kUt_xj8KLP_jn#Wc6_hS7d%%%cUTteC^trR{s=R%^*;P?$$=;S= zb~5Ru)n!W}oBI10YaTj{>m@hu1Xnwq3tXFX%@1WXyy>!3Znd#TT$PG6PO08ow!9d% zl|MlFD_@eO*6OUBHNV_dS{j<25MS;rtBx9|v8tK!NY_~=cnK@WBNfY;YbvU_B+m7O z!1gd*kdAaSv~(QC$E=jAy)H+un@?s6CI8M;%U3mO#37i?@PZgCa%7ZOGiDBDs~Z%l z5Sl4XOvWhx{c>pwN>>RJ|4lW^Z{B_$m`pBt_~9Q;5+ADzk6Ptp*kF685xTX7;+!$a zE_*4av1^UzRg5=wW1W&%wgl_6goLprVUDWCgk>6$8O>V4TDURHm{)DFEe4VAVt5`> zYZZUq9os_n*r$qSoKKx6JLZ8&*0r(8A#JWE+5&IX*cIv7fp+^_Wkj_tJzHWM32njX z8+kFdg)s#or(1amv@nLZQ%dtmy`V_iiH?24UB^hqhnKR3N>OBsm}awkHhDG9EbnS97L$C zg#3p&2>lK#J1RMPA@LVQ8WZUD>aosL z`NeF6L;j+sf2w1AS|(y_&2pV}9W3k(stnguQz^blHdy&iH!;H!rqcX@pEaeZJKw56 zjVjkQs$9#$uRqJGT+=lXjy1t0l`rpK4NL~Dsl0lI95!WeRc!VTa@W5qzWOR` zG4dkSj!y-EAzmK{Yv9I}wx>DX?OEHMuxxJd){(`w;;F>+SKGL@FylB#$3cZNg&9+T zT@xUtaN}5D9?-&#qk&mK(-=*_>Xl$%qkyf1trBD$0qj*J$e3UcHqkwrF|vk!X)O9s zV;qXq=TM8r7!6E^T7ryvU{8P+WE=u4;3sG|6xbTn9A*p#HWM|6t1^4P5^m(xn(e5W zhfS()rfY}F-0%JTexRR6|1bJE<8nU_pymF4ikJKO9%%jjoP4>Tp_luaez~8_75#i+ zpr7W;{oI6F`uiDoxu1_+?q|g1euD3TerhiF^P9k*`^lKuN~+h=PP1HC3uB-*Nmb)8 z?CzxVfaWc__ct5wPZw{Jb)ky1#DBpnHx9u*hxDXI8{ehlsjWG-M{_Pl<`a=CjJbxz znpUJc{Aay2suT|s31%9=$aNikmj4_)k4Y0&^3LCSahgfhihKy09 zHv?7?mPj+TM7&;oqqZQ#6l$ba-Qqw9=8|G}2MT>vHFZv`8t5inW4K*|Jc{HDXa;6` z4zO^e7V~~fUL(o_$gVZ9MqMM$Tmh|#GqM9YCVmJ# zg!;A6jgcZvw<2A4IX#7@qrAScpn#wT%9$fTZDkg`e9$+DNq*Rus8^9DtEm**rKVEs zq?*bD8?UBPY`dCDu_OL#?ZiZ}kpT{II(oFBFC8=nv>4DB<5l*TF9>rX>wV)vr{kal zJyczH_xCB%?Fwz5>exQC6|7=ksI3NrcFrGcGKi7j{eVgf1FfH@a9}4?ng-bSYFUcy zQfWcJzENoutMf;oZCc;w&_%)O+S>x@3{ltKe$c4S?O(%BK&Q}gqkD?Fx5OaE-WTlX zkPjPdK2qI^xI^|CY~5sLE8>1S%ThXahitZ_2zieqF9X_fq*I)x590h4xb6yEe+6#1 z0v87GZ&A;FTdVx-K%2CF$_wSIzx@umdjRhS{YoBBf=+oqIgo#_AH>*CC&mt$7)?HR zSu5fW<0tK=_R(5n;P$1j4_2Mk4&eGYa9S9s|OUJw}8dc}xgfJZ6L+ct#^^ z@+2YL>luS^k7q2xdQURKU7i$#Z+a{UU-OJZ__Aj_!WTVPA>8h{8sRoiI>ODK41~{m zZbJBsX9B_vo@)@U_gsr`o##4)k9aZ>uJKGn==NNX@IlWcgv&jX5#Hy?M!3Y2gK)7& zM7YqCi*SL*j?m@FLs;R7Mp)(LX>dow{Zc z+h3k_b9xWZg+ZrD#5qT5hreS zk<`9p@yO4x@oG7S-RcZ=9nliULM!)}iLXx_%@p2_2I|ipG)PnzODX3o}wv-+SKY z-i^^|$%Z|nYosgqS0xmuM_se7k*?KWE`QN={k;MgNi)u{S+O|9hKZAKI*eb|(=cp$ z`ZBgb*YtA(Nn(r*i@WY8DgJgQ^{{KpgAe-xn??otYfpA z)=ArV9Wn?RUQ9LQ%=f9BuqA`vxdY#sjj`bJ*!`A$!yU61Q3;qmZ}y^RFl&X~Tuxy( zn;q#$SY#3EY;p9=oklIx6j_muDfEk4sY!CGXcTqe_g@M*YJO6t!8V%VUvG3#&Ya%4cdm zie0bLsHM^Z8nsnmL#Zte0k%NRN3r_Lm{Dd-L)2bfj}aQKK=&zhH@XjWUbQN&!rnRuV91K9>nGbct!0gVL#w zujTxr4T=<@L_0n=%`PhjV9j^C%eFd?x@KZN)i130qbl{NlQYexE4I7V*1u{|Uzs9# z1FS=b7U(*pIu4quqzW2(2t3KeNHu3bIuW2TPEuWyNkIBGz+8oua8-0$RWoFw$ROU{ z5J2y#O&O4)aV{{3)Xxx*!dn8;5al8~T{Kq`a`CDZo%IGJR=MO26{A?;;?Gle_jYwP zTyU>UZc(LGU1vPjnPSpg1JcVMDyBqh53MDus_~Bb+G)HiOxR@O+HRZwa>83S=?HP@ zgf;Ocwvwq;%`ZH{5UL}1ahNztEId6mxX>4((~N*FE=>l1e+!@uU+DpS16 z8IYE$JW*=Z6+F2!GXKESGR5BTO6AaQvNRs+bdal?0-i1=q@OF_qr7Q`%263OxX@NU zzg8wz+ADWfx$~?SJcqCYIf{_u=Qh#D3_B<%Kb0!daS*%pw@{AY?Lg|GHaY@_t{8!l zfiJvL<0uinc8>Z1hM})dDwz$>!|$Gsvywragwy^+g{${9|2f}sjOg$AsAmBC+(6Dm zwMOdM8jcz(loB;(n)tkIFeHn&2{FR;Vz~hQq2^~DTq(R>>Rwa7v`W9HktE;P#+ofA zy)g+I;cZ1LvgUF4qG$KlluTjN=;7v%ci#ZY2s_!NNPkm?n>E6X_>NP*KUB2%z7F4H z<50WC3>!t!2ySx$D3Pe+VfCvBU*Y(ANYONGhOduQvGD5GL+Zyghm6)v>(5X9Z>yBp zCT4PsPvN`U+4_3A&gK#!vr#vV7w12;+QziOw!^B?*b}irYW#QKo7o(Ys{K8xuQZvk zf*yK7Nhiis=-d&0il^A8o{MJ!(l9?`90O{JUomAtvtiAt>E=8$aIK1)@=U-Pzp0?G z``wx~CSo+$%OPiSUtzb9(Z90IPO~e~p4I(&xfv;)eeng9{xC))ApP7|Wil9t1N#9d zJ>lY$>{??$I?_iB(fNi_vn$KTaAs$uBObN^-Lv8$F_T*+FM%cj@H$qo<6dI|?FYWC zh-sfD(>Mls0OJ5WfX*Ksn-IFQo#T3=2>jmq{k!4W4<^0{!ga3UNM{zQ%Dt|U-I-j# zo#F^&uJ3cR8*^P=R%|XW3rJu08O1MLB|&|Mo|iR9KdkVOogiQx3dcg~NY-gSTamt0 z3|Ts7vrNd23q4NvZbfQUG|0&!=Z9(;T6>f7Jnm_Q(^=_5?6%XItnhr4HanaIV{mJq z8>jYw^w&P>rGEx3Qk_a~cdP>TlMm3lk}!Y{peTK+USh>K#}U^$|04-u`378+tS(=>%|!N zYMFKat15EwEsLL3EgIT(h!L`)&M?zU(im_W*f)+akUcuPwu?WMPOwjy7Ld;OPEhAt zICnlW9l1JtlT{0y_VJfqL%+2S{D)pzE=HBZBLjFc@^=j2R1&}HB@>C)Z z_L}KU4=nKC_bq&vUHP48r#opt`lp{UMHpjIs@>nuLt*#+sR8LFwFM`9Xc+BYXs4-> zcFIdYdY1CnU0X^mk%08gzGU<-7GMC-zD@vS0wx2(0Cz=|ABm6*>D zur}cf&TO1o5Rm4>gRa9~HU%Sex9UaEfx`x1lpc@-)$c*E5d;1ax(@s8kAU>NnvX;3 zxPeq2Rtwf--#FE32}tYF*H9xJKR^0FZK8m*S}jGf^Oyr_wFIPP7zt{(1f)}HzEEHb z)qE8DLCr_onXBfb*r8scdPxG(Y(H+U)P4DjUTC@=MN)bgLqIy&M=g{PwEbN#wNL`m z#6A`lN>$-m|+f;v*YMW3S1wIhix63R|8d$N!$3XtdVfSA0l9su6voQkz`?d-; zd<(^^8uimC*K}{49$-vCqE5x_zt!v4!%7YREvqv}p- zht}UnbTS|U{T1x`#(?xd5A>exMtI2r(!D)5IO4@wu3DU!id|1vI)vzMq3}}s6qCuA ziP5c zM^|#bOfD9uO>iD%R5_1By~0e?T{IH8qG+Geu`bdQ@D++LdXfxC1#0W8>dOj9IUdpy zhBHAdfSwJAfy|_K`uX$cxtTL&kYwPBvU26sgk`M2!-y7-bF^d}@K``1zyQz#ZUW2z zlmJ-AVHcH%{_`H0$vOD^*-o~rHXWyFdOw18%*?zTPSlCBM+T*aAFgajV72*KxJmAC zc3=l_BaW_|qwV_!`3-|*cxYAXEjsFCobi>gY1LFRQ{#xQd|DfHh^7iiMOx5cnyzu0 zDcg~gS0`tV&CbuUx5|^pQd?RW+bU;|wZS`JAB)kkwaNu!LvdC!;RHl&-BwXS)Q3N#{COF%hYDr_|md z^Re6oQa07cGDXfU3yj?)a!YxfYpPFR;+!;gBZgmX2=;(j#IZDeC>^V8pw@r`kB;JTZ*?(;7B|+Wgi=W+E>t29iRKTARyn z(TUJWd+Gep`ycAb(VB+jL9*m9Q+OWV6-)xs z6gkv!$P{bb_K-m=UwYPDx0E%XSQ=qIu^1kmYI+A~xHrxKZ+MF^1>$wGYZR@E$=+$M zwQ=oxde-=aV4RNe=BHS=0Pp;$f*@FzBkfpqQv=e!WwVPhCfd!a)e(@sm*et_FhUGt zY~NTp95&3#@+_Z@+gOU*-}Hd=uuScxG%*sZVWnIFJBQX2YVWsN0@6O2!5ts9-ROzs z-S?vx9NSG_j!^6LYv?usZxM78ux9}DfC796JK|TyiOCJbth0ySM^Bb-wJ@ntJ8pUG z@%L#QakWfZH_Fm}5kjn5KsI!uE`MX?@B9Thwk%I;m|6KX&mAI$%@~=E7K|*~pylXm zulNG3h&y`s5Rr0CrVgJbh=ctuld&s6U*9256lo7>|03kOoI5LNzje4-8fW8L)F>f~At z84ceoT}A9oVfTsYPa#HMm=`DqxKUSBH1^%i+OC;ru0lq^?Q+qlZ_*ON~ zW2GkGo76mpJR|TY6{AS|KMHuQG79_82w-azYAbw&8#=%Apo%lFZ2i)GDsB{q17EBR zhn3=nzpfCr3Bqo7)YbuCU{eaweGFe<;34hEyovA$!ltEiVZ|8P5s$++9X|uUomY%s z0lw*&@-)UeqkGxS3XiuoS|bNZ>A%}~^20$n8Rk$-nD6~V?ZfhX81dtVLZ2lortnLg z+lV=Odi1oIf|&eC&J3r;X?Eg8mNVM7OPX6<#JcXm2Y zI9r`xI6Is^dgg$hru*sTdwc487`XQ)iNJAp1b?JB#k2}{r+(>FpmHRa5{}z&#>d7P zQ(QRx#}Yr@B2Ztg0oX`&^dJ+ZeAhU9w*|^8sZ^DhX2LJfHnl&0sG>Li+e=F)B9D6h zU;MqdKb=x~>B+a~kQW|5(@^NEs}~Qo*U9@0F_5H#259b6=_Uih2~pIS^EUhkRoE|u z#o?H<=K{Pj4Dk`f@SF&K=^J3dptJ?3?sHei|E~LZA}qxm>UdJ(Ch(sE=$!ERoVmD$ zr+$4EyF_2<5h^+#<7^sQX~ZkRYbYp$_;B=u5o7WzJb^RdCud)^9j z&7RIrA|SDx{X28bzQk|Nu**5i3uh)Q&urn$-Df{pu0vj(x$CUHr@W?a1>V_lbv2f6 z_nhUIcc0B&PU-d*AFzWc`B?lWgg=}aGhci5sno0$@>%clW8_55YiD0wz7{F8_7~2K zLdj3mlKO8ioZYWt;fH=W6Tdv=`GTeaOgQat3Pls?qM!FK=Z46_`kDZ@*P zalffpT-z2*Z+fXVzv=i2l8HAQ2K8N>5DN?7l9w?^Ug|lZ^FZf=kO{U$QERRzQl0@t|E5U<}9~2uMYj0@A2UEUf<+(A^5XJB$Rj zLgCdr{W}BH&-<>z!kETFD8Mf%B4dww1K!FoZF{y}}sv_H@(^qi&Y8T3u-;L;LY>vY@&cQJvr zRI9$)i+AC{p+&}iiCHy7I+(60VZO!$*DXv$0e3-P!JjUepy{*?56hZdx@1P)aR2!8of7)_UZYG9j&1<|F^Bh4&e3y<+N_ zt0{I;j~46zj72QbIfIiowOFmUTB#RA>(J#VxFVl`ZCSp})p|bM{8@bT!;pXDYI%&l zzYOJ%zUc{f%vzqGIJd*$)c-nD zufD&bZNoBSf}j;?TeDrU4b%>&*1QRE&%jvV{fSvqV0OadabZvHt>(|E zqev#d**7Y(Vb}N9VdZV?O~AgFP%yjNFQxUxJL2==T_eQDY}&S$Y?`)M`)V887KgIW z^$c?e=hoN~T=b=9>-kV_qb7g4Ou9V3^h=Myq|Ku; zpWJJ0GJwkGQCap&hduh{<4T0i4qLSnAU9GO;?+~NGR80M@8M0^c{^hLQftqcD$6ok zeqGhEtl{E`s*d?}PRp`Zi=%DG;oX)S+BApjEm>_1mZY{Y1AqL0rMG?KLyW;(B{y?j zR`%=Ny3})r+a~EOza3^?!uxn1X}Ng<{>KJzAmfj)VSx5L;<1inxVK}W#j-EmRh8sm zj0po{!I&SFtyZI>x9o4;Mwubc$X58$;G4TdlAGJ^s@mrbGbN~fzN@E^=CCrBpmx1! zxJtXNM{j8Gs_$16gW)YT-Q2)fcC_mq`h4d25QOoVua-;EI04X+%j$_XZ1&>LvkFlh4Lg$0d&tn+VZI0Q?LQ`uR6UB(bp{dc>VWWI9i5mEu zN#?7V1tAn{D)Et3R)koXwJ!0Ayj|eDuJ#Gstn9)&)f2V5WDV|>ElO4*@hR>fYl&%< z?e!K$T(^7A@ihf&^3AR_uvrZ$ifQ$60McV35{euR1|M-bz z%%mZfYg7wD7o9v|!l(%Yzm2%DQ7L~H?q2!h*WAcavOTP6|>MaF1DSX#3lH8I5 z3uuwwJvsI$P^0j=ljgD6!|)Ym{MCD%BOOvcWqN!SZjP#M%*X#-%t;Q$TLu5- zsv}hz_+xPw=HG3LUCedy+^njz?X-M4N~dFA`ckbe-5!MRAmhjC*VO|peW-pJ3MPX8 z9pHbW@fmM|o$kSGm96R>Z&~d~$L#VKy<2OC7qF=-yK~<;f|~iRBP0g3#TZv0MUxuU zrcKqh@u@NGM{48k$w)~y^2d+V#@e?7-;Q1~e)=}+NNtS$Nl>0NUci5S=)`kgJI-S7 zQ`v-`6Wn$^L=_qVjzG6gPi6Kl(B}gl0EFRuO3$dZz@G#>&Je>H#Cs4!;{xcrYTwjw zamaMMH!~*rI8BBttWYN=WmApxmMy3~11DC3TU~nEip|9v@dSB(Kzd5)ozG|Vmi6B? z({}Mc4_U>9H#y{GH;T{6%u^pMTLT|)_GvPDhGY5tnPQ@Q(*4u(vQD$+>G?m%lUGH% zwI;vx4_PCA+N8Zeoa|`4U6dJ-q&(0j;%?$QxmHdTa+=iE@R>__w zy#F?}2aOwZL~C-1P0)Qgvi2g!(Em|b7Spp}1TQLV+fnmP=5oVXB z4PK#STlbDKSOCfCoE83md%{;*-J!xfXrP2Y>Ka!9cj4NyPqoVq+4Bi@_f&lc{v!^y zM)@Hvb0|AMzI2-_7MLQH`pqt+CBW)H9d^Wa#4hMDmt`?09LmI-`pN|)rKF8XakiPD zk#rN;3;MESK`DoJuAj8$>B_>3`>P{urZ&MsEv4l7u#?QAde-!85sT29>w0&tPn}=a z8N%deuiKqk_Qu-ECbKK0jM_@ncSda`*gDXS>Ps<;%1s=#fjU&(nw(;$c2ez~&O1t} zm&NxeH-2UJ(s!cpe-CS5n=JAMSyp!6^NvonpwITq^FHlb$3ObbGp>id+=VFEIY)!) z4lySBDz>X<`v7e#omYbp1Hfd`xx~@VIk>Jf8-sAyMQ%PQ*UtF;rd+SQLSN6Q=Wv3v z=CA^~e@&Mg!wvgb8erMs=m%()Ylow zVxRPWfO?|(Q|Who;4Jn%y0_8&&hVlZ`f>F>dglK`AuT`1R^0PlR6^}xd13fZHum^8 zB?o8l83$w6+b(qk=Lh3V$Q+M$^64+xdOL92B~yEjRdp2YAN@Z`8oMCJ0rbAAkuj{V zhppxBlYW7PzM(H5RrZZ^jLeU7Jq4)#iloqFX{i{=&j6SI%5be-x-D>-#J!l(a`RPbXTuhR7r`v>glFxEa_elqOg(mvV zZ3zXXPipEV$^U38$A3aVLqf;)_gl5Gu+J}PQOGShGmEoX+zW76Z;bN)>j$y;ufN4X z_#a-tFM#jh(vBI(^2np>9(#QK6C0j-`k777{^hyNTeto7`Ry<4c&YB?S6+SX^*7$! z`PSR-)HgKj-t%tb-uK>b@_6=r(EQ8Cb4vHtPL);;>j!)sTsa<6>w zffdW|zwh3qOKKKZEn0Zb-SaD5^X{scTV7V`yz`FRZ<{lFR>`fm+ zR#4!`&(E{lb8|&8Cnq~QD=W)pvrWZM82n8cLcUbKC8A-03VWE1z3&*F0C{{JZa2xTtDz&61_}-gp1<6%RbP z(!FZ++J||_E<+fM`p;o>=y%1#md1je7DjYW13m*7Bk)8l4*vs=L(e@(`vtH7;02rl zEC!r}{$}v|G)|`_93g2}_t(=Nj{jvJ(qB)o{*UzpBhQx6@ZXIhx(jIM71TpzGp}$u z`9BFIS-P;QdeOa=HI<7NR?G$CODbxXR4$yCmYzXK-K$%ECD}Q*j4w^Q_L|btw6yWr zIkuazW(oN-pFa-pISYJl0VDwi`8Qs zy{fb_gwP*NkAd9Wnm7IC;(jNRN_WHW!jNQ=O)AK}V6Pl;9@txgxSHGrCMhMC+ze_q zN=zp!K%um2NIIDamdnX_Ygc?3 z`omV!*~!wEg^}dUIitruTU@_k-?#I>>pA>-+OHd%_g=r?i(krqeXRUQ zWhbu2TT-X}dZ5qI7nA&AW$(g=wvOn2bJc^XBxA&i7b_yVqlQ0B-X8Vl>ahd9y?+!x zN0cQAKiqw&cxuI0-8GHFYbVEd1g;vJxozaZz&pB!E4MWMvv9jubk*(7c|F%=Z)iWT zkm@!%7XOO3$HhJRQ~k?_ht7NOrrfvJ?YsAyb>8VO-}>CCit{@&GhQt@c|Ll!3zF+*0+K9Q2mM$E!dGA^GlcCe* z?-zj%uTo5VkVQsWTGY{5o0hzGTGlijPo?BGiHWF69?kbEkgJ2d+K&0ac16|_xrui@2_gD zTYJ^1Z=E`I>Qu2|YPnMsMIrRl37G=LS|$pHaqu%wOM)N^$~eIUFH=Yo5?Q{WeGMpL zD6tCyIRz0)*ILt2#DsK%M-~LnR6$TFC74ffIMWLy0=2<12j{aiL9oFmCkV!i^;w*M zI0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDi zfFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L z1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861 zM}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgG za0EC490861M}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL3 z07rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h z2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDifFr;W;0SO8I0762 zjsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDifFr;W z;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd5 z0geDi;C~qbovCG_U=WM`(Oue6s0^it!eK)NLk!$dxD+@u+$gxQa0PJV;U>dPg>%8p zhPxK-dbk?6I=Gc^tKn`OCKxus-2!(P+)YTk4UYP{5B?UoZE#P({RVCq+#a~s;10mG z!@UppN4UdqpTqqbt_zN6{sI1JxbtuUI0b!##u0&VEZlH7D_j;_F5G2s6XB-7O@o^O z7pk)ZVQP2qZ!Xd*;TFQx!YzYa1@~*X-@?5J_cC0ljgY@Dn3nKQ@jtPjN0xHzUV$M+h?`U^2sO1B~9xQlvhqh%eC;QAXO~LasEBwWY^g5x-0J4 zEjyJhf;H@6ohQ+`-J$b}p_ggE8rfhrn@0y!O+q-u)-?<|mvEMd#_za?yDRu1b5!3D6Pc*M^y3#h} zuG<0{mpPT5k zKTjp|0`BK5&N)8A#EE;viI~g!Ihco^JETMBj#9opVD;oX#fd_;u1D4te3r6H>4_JU z*ADfG5f1x(4#_8Gdpn1h3D#jq8R8RDHal`V#e(MSbDg3o3V2{{Z<8|PI|tKu&VP}< zZoeJWxEVDk5Pq#oFt>Dp5<#3OM+2Eiolh>8@s-NuaGg(A{(6^S>g*EQc0-O4Z*}e# zdW@3LGfP?OjCW$DQo>qyIZELBI_>6or#PNy&sbWsY|{xv`i*CVhx|<^WU1oB>Bd7f zKXi6{7yoR>cf-EduW&jSIafRPJ3F1%`^=`eEZxcTH9JbIU)+Jx1@;=d)YDQdolPr{ ze%ux(<}E0xIPt1(r*l@;OlMT*+>(Zp+LEFYsn&2ZOxjpmT>DO~ym&U_?lI17&Y{jv zyGCV&e{m;zrSq{V{u z;CDJ}{WRMo$V%(fCEb7iq}|ujfwdCpyf0Dho-DnZIPK(LZbYdizi|kiz60?-aoJmm zle48BQ5W5$>kN3O2Lwx;^Y7qxZv|@BVx5M6?fYi;4}xg9e7fwMdg7YZmi!{ z?DVwu2r*LkWP_pY^`63*t2(ozO-|7g)uR{h^XY8?Z(ATca&Y{$Kz8`x_)CF-*sOr} z*+4<`KpeH73>5sN_SQhbPijASv9>$ly(=L1#7mjuK3gUi#70X#WsKxAq{c={K7DFz zxa3n)cRIQ|3!M_1EzwfxB<^1k*a1G906AC!+-xn6?N-2H9^@1UJkD@u7DCq{6!5MN z#9Ktv5xVd5nViFS+YShAZKo?9P^PP{4?Mav$ExFsKXg-y(AJMOg&%}tve2u9yHe_w z?GY?tu2`p-ztyvIbo+NYqtufqc{>$X`*%XD4l|rwRX+`NjDp=4l*RSakYdtYMfdLF zjk^!|Hru3bSrWI1I~M4tv?*nAo5g~-X3CW-R}Bn&!WE0l~{N1bhE(PH0b8( zTq*0NJ=L#LoMe5&tBMPA>z1d_U%%NcnzuFR_I=+~KaKJPS!budFa5W1w_v57H#x*Z z)Q`ABEI8MtE0LUAz)khjASY7JcS)nWTa=?yP6$ebQ+B?n&+kyNI`d0@;TYH1ePFK7 zxZF6Y1mC&7$mJz)bA1N*2KX|3d3nrOxyL9=Snb~)i3?lV9V5#ZB#UrT8YZ5UVlCa0 z^r#^+-JAHPL*KcwTauGXN zOKJ*Xs51q;e{Yk@4tcC61fjznO1CH1Pb*H9P8RFhPyQY<$Y?)!It(*0_B&sN;!JQh zyqZ>S|1og+*S@AFE4m{i9`S`WcRCF{qEyqM^K3jJN)zpK?GiZpi$KH$;q-i;WV>h2 zE=Nqq%zd|`?CV%_x=t&b&ja839uTh#c;5}Yy7${IA?H*_q;+0#*hxW}?h|Is_sq7B zx6ih3_e?0RbZ>Wm-9_^|Yp7rf2S2~o7Vdn%IN*J`t-d9;To@TU!|Hi?Ps6Lr)>nJP zf@KX>Pl{)*{V7kJL+YqF(~Qxs4J=zPPW*iDvWB>Q*{px@a*U)Pa@^yNW1aO_57ai} zA*)h`99xo8EQobK`JX^aO|t-ZZ2M2cl}bUr?x7) zWJikCE#_A>R5!)$)5e%+9Xdn0AY8o*RPAyv^K6}Fy|rM^^PcRw?GD{hPhG$}CeZL| z_4>JJt>$K;ucoP(wd$^`Tw3F)s){H{Nv?5MHw+23Sbu z49Rn!UDqr2fIIT7kkU!O#~P?%qprcI4}4o_pZB*yR=>VQaYkb{qsrrLC{a+;AX+8V zZP*j=RzNc4IWvLse+<;lM(uk0_`i{4`Foc}gIFkKCLH=t>P3%=&~c*q>%b zCa2sZ$}+gt=KN-FVjIb^_X5h8ZZ=P)m{shmbh5sT zx_OQ?NDI-}B0Ce?M2m`&&4H(o7AeZ5lycx(qdnkV(I9N*&W}rwi^M8!P2S3H( zU(7)a>m~f3%t6He+8h{(|6H=|y7l6?>l(xnPdQ|8P^vQwd+}I*i0_DVjP}&O2D~Hv zXS$47XN-R_8&TlDAv51Ih~0WIW>n*PgY7P8*bgusZfqbfzRG8^$tRlyD>Px^<~RCv zmMpgO-R5s(eBH?S+Gqd%8z19qS`$%N6H-RadTlj`1=Lba!z^FqzJF{UHZJOgHDN6?Bk&ZL2H-aRnRn1OME?%+6;fpLE7zjU0URaZxHE$@U*ifj{2 zq?6RQ48!g&oP0}pf%g6b=5_hbt9*utfVZjtY?rSk8v7jbGg=&Sk3l9~b5yVLW4upJ zMX5;fG9A`*!27fQQ(cXWi-$P{%wA!CmNTF6<^jK^YrK56d11`wqSaDof+n`hzNHv) zNCzrL3L^vF=lwP(G?4?OiF9=tTlD4OmIyOxb*ud0m`e&R_g8e+H|}&})j&2GTB1r7 zlnHpoM=w_@f~O6;Lu8wKmL_N)LQ%fL!yuLGsA}r5976kg$i`SF<=c>N7|73}eAG9#l$Q%=fhcqNNmtf^mN)uBA#XqQOSBvC z7O`9ky~uJY^exMk5gN;KDfBqYrO?s-%S#0dh0^>I{Cte)fxdjCiAak_nrO}`efq3@ z5qQ0CEYb-F1JWbdx?9&5@LnIF{z=C6p|3C&`jGV+hO{61!z?CeENFj=r9~o5qbUlZ z<19@<=qpy2LNBs36`@aA8ijWD$DnU&?*qu9Ft+x#fjh(5+B=9elDXO%z7H~maHBni z?Je=}vG;|!4*8Cl9Nj`gJN!ewZjh8A>Wx`_zcnq)A%jYiS}=ULg2 zXbXjDjG-{K6RJyPLUn1p-v+TXnniHcvDR;as{qNKv|q$`*nX1sar-5Fjr&RBN9|Ye z4d1WgtKYB3SKe>HSKJ?lufH`M-wUk~`1)ES@jcrbh3}88(fIzoH3r}BTaEaB*BXoO z-&%*@d%QIP-(#(b_j-@JwPxV^YU@aRUv3?R@9x%2d|zzM!uR>sY((56A8*aYcSmbJzFS)h@O`xPDtsSq9f$7&t(W0@Z|mjw-qm^qzIV13;=8GJJig7X z6Y#yIbt1m&TZ{0$v9$!>rq-$Wu4;ASyP|a(zROxm@%6M$$G5gM4&Un5q4+LrjmP)8 z)?xTowmyOHHLXwLJGXT*zO!1(@SV}>!ndro9N%fJGw>~GorCX`R?^`r&IKC8@tHVb zx167PZear?eov$j@c!=nWXK4TP0yVdtkx>y0n*KHv}~`&Dakz_Csc~hTT1dj;63jj z>bl$_j1V*8XYTYFGweTRtQu0*?n|87?u*Z8wzT_F5gL&}y|w#HS4V<#ZaJUjq#nZ& zz7f4;Ic?~z;rzHNy$fdy-F$cb0n*Fq9Gqtm44LpF;ewbYbI)TOOlK?=<`uR?m6xCU zs9168Emrr`AFrM(Kmu9a0q^MZcF>&Vq&;E8`QsiTyRQ>ne3U3nu*Js z_a=LWAVe)2&~m7)KMNZK!sKv3aiw?`+%>NlGt#*G!s+hjy+Eg}1p17jg;wzQfe4%) z4F?P@v{wH*5TkYdRRAWE131GLC*l+v>70nuVe;DEJ;P?^uhs1_?E7YqkRh7)tm;`O zWc7E7qmOvD-Ev2_7IQ#?m+NyM4WeLiZRyziA&IKghpa31d} zLy2op!hsS$bP-LYXT`XVtP?W(J5$z**}4v#Z#wFyMU)Nk4DrnN47nw&Zj1e%;`pFW zLZeIZZ1+^&B3g&k-2+{rr;FO4wje9oL|5Wk!i3W5rx{zq%1KwmDR=4CY5G#X_W{^J z1*A}1Fw_2H08Mu?e42l;RZv}vGE+R6ZHr7Pz;Kb35Y)N0HdC9_$Ftx;@bD^-ydEw7-XA?~Uvwor6NRz?!9ne!)Vd6}-&KQOJjo$TWqzVD8gro1sB8`)olC zqU${6j|FY>P(ssQzGSsXXPN06g>rg?4xyZ$Y4atl9BC@=vvL%gz|u%lX+s+6D$t== zkI@J%W92Bcdmv=?iI!+KstG_SApqGIpxx+3q{~dJ$id!8DR=vHCINNt^a;VU!HfGa zKcrJYmk6Cd4J~9xJLGF7T>{dp*@TsZ5G0?+0w1!(GR#HkWaDc)z4C#8HzpA0`oJ>3 zdM+H+{F&zJ?e5P!b1G#IrTwrpN zox$&oYV&)e1Lt7rqEZbKgwad!eUi&)Mb;nQe?)**cT4 z&H@&r&F_8s&44A<)=RAv*SGv^seXp+iL^iBkUFkg`gF<*4)0OHW3X>Zp68f1rT)MZ zcZveZ2-!KzneMDOF(s^`JKUfQhb*qj1bu(-qYs4M_Q?~q1AsjNk^Y$c$@ z+Q2+kGSB&#&t%GUUT2TDPjJ@QAwO7oolB~M)r;*lyI0p6Uv3dHuk6rSvn@t*1|-7o z0(z&;ItDR1yZ<&&Xip!JV0~xrB%};46&?wA{}4#9D)uW8Ph#;m1A4@-KwQ`|v{bP| zM+qqQW@|Z8V$sGOEH)dlD8z0JD0{ZT)+f~KVAbCZt{-_Ie1v|6R-Wu{w*?aSiAC|< z0r^a)Zuf3lXNv@TVT)mg>|A>5Mu*q|-43fpDNV%+Y3yIVrtpB@+tA<3Y^9lk&TfQU z2;>Xq(U7^r`zf4NI_1Y?(eEABFPcXpbzXnKQVhw4HOKO;)2#^WS=ch&g0R?cDX%#5 zO5-MrU^bQ3fX{q=6=&=P+RC=Nt)A4<;xo_ISdnwGFS(r3?*Jlx?>Bw*7Lz#vp|5e$ zv!At`_R!?!aC|TXHu4L#2v}YxQV~W~}njlHwWP{FLihGT5G!DdV z@M%m%ZB38$UyN9n>40d{ZW!M(8ThY8&=f^>3|B%CZ3>Ze`NqT7{O zE`28n;JLmJtj(C~n(DdMnrgrIlRmTaL(e?5@9<+j1^Gt;GI&Qt=ukk?f$N0g6LLww z`%%DDY;Yg&3Br?Sd)>`@1K#$4f|5Fve3R9o*7pS-!#%D2L~(v}spEuF994$eR+n1< z2G{mA|-H7YS#83k3R@T+%s{Tt_{3p)Af<4_12K zI;}_92hiVJr)3j%ia@hd?4B&hXM5?C4DNmZw1N`122_OTG_wv=hIMZcps@$uI_=2o zSY>p^H*fUmn*XytcGWej9%UL0^&M^oudo>wF9>-eDE03jBZ%NVnq9}audBwDj+^24 zp1v@S&A0u>)3Ny|b@DK z^pPe{9QpPIE8X-!1HZU$AznQ=o?1#fso(pje$f(RPDHKFevO8TGY6;my-%?o+_0em z?F*1jQ({Yr7QgpVqVLS+D$+#!-cR>uVtk2kCO8`FIJiQ%BDhF6yvAUZNe5@*fOQu?_CNDx&eFHWFWMhSrH7Na1TP1?)Tc6-Gf5IgZ2?xhnoK3 z_ddqTNyr@&%$1?FU`_UoVOopddoRWsVJ7_gF$U6!{N9bM7KKh@4oGY9d)ER9(p&uA z?^wAAgjTR}6#AN#qka~#auhmzL10!AzjuB=Zm!tA{NV*iy54{{&?}n!-f#OzLkUOU zUtS;$#qS;8r-O##_g;o|tzp^0>{U!RAsq!a5a_pSv-c^`VufxI{8t0L_k5Sowg_)F zX2C(ft%nZZM&bGv;|!vj_STtx(V{vHEL@7P>BC@WI7ky!KLKi{E>5FXW!%N?6JK-Zj0GT*=N#&t{yM=6W8gbJ^q0 z*elL=PPSOgg+OO9$~Na+j?iy=FLR|jlRXbfq$xMg&O_Q`y;je5)XXijO}8Ol(Q8A9 za>@|OW1*?N72VCNXl2~lJEAPkvqS7VY(KZ5q12p>w9C={scPb1;)C4YdW#))XTNuJ zFWL5J9cA<;l*QFa-Gx#jPMe^7h?!BIgm&$7(Dv*!lo~=~%E!9MOF=9GF*-^5z2&TT z9kXTmy;EC-wn&@_65;4DGy}-P@L#&j_*GYLYxty6s|;oBU?@u&YI54@iWc zNd6SupisEt%3O?QM}L@TQ$3?v&vX!I@4TltlCz!vb%XoWmWX_ zZB<7-Q_dWmebl`TEsNqd_gs&)TPMxMSsx>XMSygBX44P^lNs*@AMB6>_wixEpKOt{ zNawe;h~s5vz$9b|pVdmwUnlzjv}P!gbh^Xx?$F$yu}dly&E7o%Q(Y80+y>u;?_<9iVAV zk_p!EHv43-*S9_C)RtJX%evVib-vuYsoNfg(=p!s%(b6|bw0gZg?5=%id8qo@BN$4 z>JiPUrB16prn>gCriCdo1Oj1VOOr;YuJA4P{&EF4CvEwyn?G zGpFtoSvoA3wgQ<$Z9o=%P(6C>m0YfO;*P#K+DTLkQxay_ouP48`>-oOUjNKD-bo{* z@x}NSyLZ*mxDB{j3iY(B3a2ffopgHOR}B29rZ#@On?l-n*aqfdT-5g>_%u!$v)wh( zWG|~Yvwn7cOS|tItealPPTyul7y;WXts>o3BgbQfof$R3QaEF$uRfy?>#KQayRRt& zC}YRJS84a%p3&~xI&`kN-S?{uqI9S4*b>-tr@?*X}|Y{0G&TJ zR*gdVnE=(um_8EWCj)tGq$3dC&dS_fWkL86R>m~ljPU&dvy;Z3j_~F{I`*I82yF_G zuJAE#==#04u&@Ztw%>ar3!9w@2(JnxKuc+cy{-bf3BG5Vhinhp0{3MJhIe2K^xvva zn?4>kLFlx^7dBv23V#gZgr5m<_aOYr5hqM#^0CfnFS|M*H40__B%a0(Z9C>c)#KcbxkpU%c(6 zs>Z5iRW((`Rj#VJRUi2xZ5Q09-6!40-RR z!5-zkRupc-#co}aIm?67f1=Qjw+LjbH6fJ7&;w6Y$vtBb@9@{u z5m(hz6~ZpiF-0pM5ipwn)=o6zA6fH_DjEPj)Z>{J}f8pe($FUg(0QGPqMGM zA^D>0lLh-~%%MRR<~0kjpTg0c$YqjsZlg?g$(ueG4mFZJ_gSnz(j73i8sjUG&iTcQ z%r$9brI51eeSp-h3yX>+o*HMn-Y2{Iy>s5ASfS0^MShX(H1qAYzjsl7{WNdOj2W38 z=`JB#=zgTxRW4Z7n=G5FHvj&lZiuy6JUK=DM0j{_Y{^}Qs^(vBp1DuRD(pOdQv_a1 z9&Z%z4tA&8eoV-k(=mFV^(L_(ke%5UuztRG$xYVAmruSM1CEtSzqB^)PyO@<-Fhke zi66$TFKm;nXHLDl-hi?OYtJcTZ%yOQoAAy~+S!=>#mlGU^=D2^TTkhwZQTgHjhc5S zKZ5VqKa5i zuMa;)Egi+|+8cM?_IOvC^KciX^r0OIy8FjJi0g5hGCj2l_nU!i&yHm?_dT__Y~Qh) zghIUGFtK-W_C#m^=etCcw~H(XG!HZ%f-ugpa>%9$g7BV0pdaQ_m!@|aTj=$N>`aE{ z`BJaNDPv`6H+Lqcmr^<4vnm5t`F`))I1x9mBB{~VZ^65%1<=NDKh#S%q9ij4{W{BV z=vV#T1JM2>&B;j1@rx#u_xruG&-=aU=XKEj`s57pX|J^1Z3#) z5lCN&Pz2-0rQln8l>up+`e7;bX)AvsY=ESXFe&)lc?KgSp>Z!%i$DA3tSx?IaN^19ZzwW6NZ33Q`fO zKw18Fu9F!E1*ID^3!N@MT$N*ForW)bqU^%n5{ zrJqKA1?<5_d%*igvNh9qAXDg^#pDderguqsDW1Cw(%I&@0<@*g?A2bf2M-Rjeb_HC ztEO0&*fTk@yCubQjTlodoi*0VC(6euCmjiq#YI(3l_lMBNz?4oJyIT8x#}*1d{S3H zZQX{px)0!ucy_Wo)RrLl((PH8eVwl*#`$cUeN?1ValW}X4_28E1FtwDT#-j3$|jYh zc%DufTK&lFhkWBQNylr4?>m5d6rV5=x@pbt;hziAN3uE*J4FB^!A%qNJR3B*RHpBd zh8Dm+XGpruO&Xo~jK7LBI=i{D8rpj7wrYD`yHAe&M-A_N=z4R>GS@iQO%^Ta0l*?r zFu21qA8E7MuCSCqB39Ny%_Hto--BdKY35_HNn!d-#TEWp(#^TY81IzI35b-rAiWyXVEP zuE5IMav=r#UP}4=hJJ5eU$QHCda`G@^KOT}<0*&IkyyIXq3cLO-Cy<&bJ>5~I}n%dMUTbz7&2W~;~Gq+1iR)k*AK zEaC9?H(&gU=pY)tI{4yOxYN6i-o#>j29hhJznUzD>HXfn_L?mE>BRGy7i{}XNR_7( zFZX+ov>Fc_3&a>op{v%x`C5pFWOk|!jO_Ow?3FDg)1OT2_qO+rtj}KSDBD^8d2xdC zc>STJJKfo9+p}FA(MR@XPwG&P?9MLk*pr>n5owZ-y_J2TbIYxw$y)C_AobXEpX@b^ z{_#l1L__x9kBCpv0>;+^X8%`f^C+$(1Xic^K|K=YqB3zpj) z&$fxqyY{|(Y*YEBGOK43bXHSVz_M{hyDu}V-8U=ZHl)}ia5l_t_th_G$C_yOd9n}r z?Af?0!CBFcusaxbXA72Cr_ot>tY-7d87bmuk95Xg16h$6Q4C2@sO1K{+xtiVM1s72 z?D$%7Vs!RpOoK4QWsVz{J}&s%AP}8y*Mb>CvUc2>**3(SIeb~u7hRNkP5IO;xo4P= z*)|m#(8_*`=MYPuRUVG?1xOD!|Jb#2t(>hM5hqI7VXXbQWb>3$;5UsLXKLvXqz=LBPAcOljYKTc{C(FIu6%I$^WHD3u zc+meO=pS!>xGSZU_F!GTqyD9?>dk4c`8B`p+P*oVTsL~dnQ1Q_May!}Q6V0!#hY(J zjxu^khkmraLmnO9d31AfX(n~2{~tPE;WKxArW8*Y&IB)XBQ)VDcsF)kvN~y8Fe$l_ru*I3Z@_6zYHG| z7mnUlcTb6OM$g21Gjm3_q!f4}D-6zz5|T(SIMmJpoLB|i>e6W|aT?x;r}&ooy$=R1 zER_o`RBLz5)Gz+#p?Ge^RW9G!E1kdei4VT9b`xyGB`1Utvs~-f6*^O!C$5`0z4(OA zI$KLIZQAhGiHqzcWTp4lK-siet$(Bg&2xE?Sq+4|(s78C&z8+nS z(rI@3h6@-|Mc^<}q?l7E^?k=yUy4o0{BFZ2}q*zZnHtHYl1v$}p<)^a7lOmnH zy!t);+QX$~0(SS&#zXjvIOrNRhxOv&lCtEg9iBvl0^X{As|R^0&^pjYDg09S9>_6I z_A1GKxDapZ>y`;w^E$*VcZUTM$uM4b)>!?ynss>By%S%4sIKDN!G>6erNf?0no{Oc z=t9 z&8SHt9q15~t(jR?(vvnXbpNc1tSsF>m&R^5v-+hW_`8Qq&`nl$so5LO-0+ftY0#&7 zmvlYkxl8`}rw@DX=#tJ3fu8f3y7RDTp;xgzy^jaew$r?t?D24zOqxp^?cAYtUD%@H zu1mQ3ZQtfu7jMdqf#1;U8FmgAaMm1FPWx9%U%&V23;ka61;6*tz;eO5ls*04F|d|r z!ZKv_M9oN9YH)6y*5@6~UT28xbw;MM&-=QcEKyo6#drHpVc(;@jrKd!ul10R8~4+h z|J{Jl_O;K3d%kmlh|^ZpeY`U8D_Z(U!SE3GWa^APN4^lE@^ zlTyl4T^XL=^cd8~9p{edpU+A+4e#@Q)DPR0B`-;N_T%rAf#yGSTpAY}frqXvHo0SO5itBsrKSlH*A z+5*BgQ|HVr(cxY|!g><}|G$1yuaDRpS5+?2VV39k2e%Iai+NKk?*KJD>i|GrxWIx#xGi@Vl3G@7c5WeeLyqt*!gtIPm71 zZ3o|a>-X*LZ@>M{JAZih-FM%6@BQ~b_~3&N@%u>oeXRY0G0H&x`|rQ^9!me=op;`T zyS@GQZ@qP}?aen2ys^Kvb>Hi+z1s51%X|0i+5OV*UfA{gbI<J+n z?byEcm%n)Qk%u38@PYg8z31+`etzd2w{N_ydBZI?-?V<+jcZmnHLj{(x#EW9OY1yK z7S}GSsjhM_{Mq%_Etp?9@7imwzH08AIkRU~%$zx+yxdh*HodfT+BBzg>eP~w;^Jb5 z!!ZRvd+0Yg6tGh=a#5zFWa?C>bK11h(&=SouJZC3GiO%JnmuRE+^ep>=Gu9c^A}uq z{m&MZ*x9rhcJ7LD3wiAXj+eW~Cmo6C2qMzTO9pcUD6*GnZjwyxJE9x6ouBmIRTe+fk z5eRRpZEUJru_Q0QKoE95v+aAKWa>3ztMV?ttg0$6Z*0j_$JJP~f^aFFe-6@F2|BmI zWx$2#JdW|gux1F+N#hOCNr(-cqvRqJ;5<5O!^vWJ1mzhdgTQPhqE3<( zRd0wi#wVI=c3El`zIw4EimLJ-M}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1 zBft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDifFr;W;0SO8 zI0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L1ULd50geDi zfFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861M}Q;15#R`L z1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgGa0EC490861 zM}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL307rl$z!BgG za0EC490861M}Q;15#R`L1ULd50geDifFr;W;0SO8I0762jsQo1Bft^h2yg^A0vrL3 z07rl$z!BgGa0LF}5s>XeM58Vc2-u(Bf9gv?5OtCmrkg5=qI@tvOcn$Qd4gWKO=lP2 z=`>Fw54mC}SF#JrPjkantyxhJWGPG@BGm{hg&Xj%O2~n$6C$O0_$!1(_^Ogk$PiVh`i>h*JR^D(!RZdl1xIjP3j~682+UYZ|p6g&q z?9(h@I6|gSBGd|NKwb^}=^(EOeuJWkS9%9wgTYV^QjI zgcl<;4*q!LQQA1<E5gFmizveYr6fWk0QR_ZaG!t{ixz2@6h{ge|dC& z|H7#wMm;)r_XGRCSo&q}k!SP1-*Vv93ClkGYxVbc*Bq@oDW7d_8$ILuxB6Ut@tMD_ zyRhQc?ZeMJf7>mig@WNX{kk^h%#egTgx{q%RN!^Jd=k!(G=WwvT<{zL^yp60TnH%)4t}s`}EN zaOEc{Z@sbjjoSX-&D>gK|LFG8jaj9u&-E1gD(-Epd^hd2KhFK75NJyIdimkGQ))jx z)7X-*xhVONKW9|ojAk$g$^z*6}(OX|V)qH=%jHOSGIh0U0_wvUN zKQMCn9WQ0yy!z&wzk0Xxj)+Z9{W(Iir#$e(^|v3-yWhFw{o@DT{ar%)&7Z&hA18~l zUQu=zHCdD1m{7L2`P`0C3m%R={y_Vmw)Ebwd*Qe3b9SGO`o{T?@ZxVKt$W`(Y5kdA@8poBXwKtkto%imWJ?@H_jbdzJjB7^PNB=SR zg`(m1dFQ7Ea{e%R+bP%44_1#>_wJieMOcJOE3o2MfWZY}NabTyaUA~!H%y^ybv7)fg}>M!-;HJ=T@(* zYFL!p*mOfdzUK--Xl$x#Xj)yrFqp9RvbAe-8mTyX)4eiLFi4Zc+&nXWR`%b*LRRyr zT&dEOpPMU1=j7^i=my1wA{6g|EW|j(Y7&CNl7CG|O~bnSrjc)oL<#pA$OKNIz zs~Tv+Oag|-0?a2IeEEtD{7XnUIaKA8X=U3w8o}Ibt{^3Jc3S3^e}prbdYdN z2NF!8qQu3;Dd}pAl4w;TbCjqwC05YMqH0qV=8KA0poAePsi{f?^F<|t{2VQs6U<$$ z7=yW5>0ER}wPLhl)?hR{SSV8k5%Cg;urVT(Ox0MVMB0?hG$qku3Q}PjpdvDFASn?| zh$?E4pQDI*K?<%=qJxcVJ&$EEQ!sZ#FjuM$rbuavl_*w3`dJQZOd1wyfs%`~G&Of{ zqM35WUYZk(|CKW1S(Bm?tAcoSvFcFtpf%d2=*J*#Q;bA8{0R64kfKgfO*xnWDN00* zFjcBE0Si%y$Wbx@u}R5sDG}+&a49)RPRLPmSaSRlC1Na+%}RQVG6GNfj}5(vP>AXaME}B~FdOkYbcLOOBFn1`aVw;Rq!^S1BB;#N{Z3IfMs>ygo;X zCE`R?3NtYR0Y{u#mZMHmhKfp5oSLJ$)Es5FT1xRSRm@RG0Hr8t{XA5YVod1v`3-0y zK8}qiQN^rbT%f|NL?e)#LtGt(NyKm&yq(zG6gw-ept?y(WfLV9nUpkD+9;wjjc`(E z_!J4ULV5^;Q9%sS$REG}v<#!Z%xsQFv7Rj`p;I-qbQ(A7Kvv{9MKps3>C;=wlo&>E zE(VKnvk_y2SDTa=X(gFZ7lTwvjiT`yr9VT; zU^5ppnUq=ozbIuIJ^w_?c&ZvbVEZ%%P9UmS9|P$wg_4GeaSvAgNi}3|*WOnoAwHOIzSN zl_Ue^fp}S`Eo@z@Lj53%2`Zgd7n7upz{*8GfcBz~T-8FP>eVPqx+N9Jk7APp$u%Sm zu`3y8MS%%YF%3yM3KmkVX|a+Nt;9;G7w>S*1NNCI@Zv?NFhjw7-%)mRc2iTO$xW-LoltV(8< z5^bSgkiZ%@$LFGXwgezCfdHf@seEoQ{x71K?|-Y@C9MopvynUt z%8w!7DoDm4Lo=>LjBO8?9?G=R2kV#>%+<0`gyn%#{>x;Ri4ikD>Jn+JUap90920lf z6hS0TRi&{s#c>E3fsD;?$q4SSl74^ zT9X!K(y3u}eN!XiP>fcuSlw8=XyM}3D{3eOqh7vzan%Y0Au^j9YJ-VO>g!f&6_zh= ztgq9ut5>dUYHV6vy>L-&&GM>-s;0V?E2zAJM(V1mLxgG?>YD0mm^egianZ+y&A_8F~F0Ap?)i19l{SFEBwGDOk zp4x^g)*?tB80SI{j;YJF=8%44Ra1>;(aI%SN6VL2Eh0LR;i+m^QQH_KKtlkOEMMNx z6fCfO`J$?UU|oJNxVUOr?ZU>ohFXG*6irqfftuwjYnCl+T)DcTrdG>hT%dKvGPNM! zUXE=e6v4SVSfqh^08AR(Xfen#3u{)cSX@T~0M+$3-hcoywR#BDRy8bNx3F$S9U+72 z^$jc6)HPyev?%MWwn2-8CJ_2Eb)+f#`J~%xdsj$-R*|_5^K-+1{vRw+Q?&wY8W`X+$HgZK5b5D0F~`G+-0`9-D>~ zC+n0H>Gx|8jF&~}P3&|gh@;qa#bj18A@0X2(2^w8%E}!GmV?v~rS{;iD8?%WN=TC> zrbybT@sC$h<7}IN0%noIiWEHxVJ}^8B0quY^E#686qVMQ6sGHBD!E#*wK+-(g&<&9 zLJq_+bQ%?Uj|BBM0g^|YB+H6bm7d$MQ4!;m?2*(XWDvBMfuNbr3H6{TF__5J#u59CrA^p zSpxldrsEM=w2Nc-AVkfT!?2A=Z5Vti#YJT(3M6Pjt2$Z95EWJs9(0jBQJKv00kjHArT!W7>2Rd44?u! zajbSI05#MA(&&qWRzgteN@e^)9k8PT_7S#|8I?(BEe)+n*o#43Ts$BYl}RaznxPD( zMl{mIG9@yb?M?}Fq!II>O-VP9&Vt=CUx`e`z69=pk`>3&puzu762ha@c{yceMe5~h zlL<|0O2XhqXq1YYq^V&!P*9+z(FBwb9KXW=h}lR#HPECWJvE5Ph!7%*J{w$u(*v}O zwc61|WAm99rY?@1kaf~}Q5i{bh9K0=2%Lw&PD;2c$`KkHe?)LO2rjnPBy|#Fr8hNJ ziU40oZ-JZA)$&V=ygjJM{##9QqylD4C1zyYhB1*ArXL12OO>t?k)I=t5fMv}uBO;T zDVt)!JiRnl1YJXw_-rLSPl+E6Li32lAr#Q=e6$jWp>Gw_*|M&_^qL z=(q1;{E?6dQ2!*XHr2z%X9k>TU$q9;R4!(VSWHv6Xf|22jOkIpJ_DJsSx9u$jH8o; z2n~q(Ns)GhItSR_ptC~c%Br+FM{%e2#sJ%XasucBnD7I6Q2%x=nMjdxCDzW2TGy7ybLtM&JY}UKhDCK)@X&S zGCJuDs*-?!%%+7;!d;Y%^U$Z~Cj=-`t};9~xcE)VaFivfqX`4ELi+;=2hG%z7Lqwk z7*Uh?V4%^ebX8Gk@}JYFXQ`jwDzdqMK{TNe=`cu2z(AyQOf!kRAk=0#MhTY|Gq!_# zM~g91at;zxq{SxG1U^zSm;j6@F&>UlW6gAungqfmHC|&HcJ{#hF#$J#$}dCYkOr?v zT_AyISK^tGgv<*ps;;KQJVhlBi9tgO&Av35&C1qbL(<>S1WtCKUX%Z>pN!(FpNwLR zHj1l4qZkt!g|w0lQrZVFUn#ivls7ejcib+K=afC@WYBIPy5^JQ2Z~?=pYOSIls+LJ%SfA^2REL_a zmZ?^>CT3u3Ttm2~VFGM8!JFx8w2;M2!bCuRW6faZ$Oxua&6xPKEJdG(V|tnzrRXzY zTAE5lh+oCaO-e2;x%bd}JhiG`OC1*_n$TlWut%r|S*o;rThW*0q2Y-&aoKH+hQYyPra-Jk zi9sM6>`D6)w!X-0;$ZMdh{G@8Q^@lQBL3K5;vWeSU!h9h1&RNqM*NB3dLjipO^Jcs zi255bI@lvKcc4R(l z0`*{})v6*Gsybt!Dz0|fKE(DV+VQ1%T6603V9u39&P+6$L{MC*vL*p>Wk~TWpn|5L z_H1GY8j=*$nM4K?MOunj9NTAUmdR|MDlN@XGJ=r|L~;;TrD4#*>1=a3>$_0NCm-vY zMMaqmRUl?7h{1UXjm9Ivs7MxdNhLEd!orpev4zf(*_5f3_@Tu~5Ic}!sN09SL#g6$ zbplnS{#b4*<)#h^=B8ef%jy;(7g`3@4R#7s=aO!fdEi|EKjX_D|{Ja>dG~MvjLTpy&)kF^Ly$6_w#4PRUS_5sy<$B2LJ- z^I{Pgz)grGvxpfb5izky8gx8FMzRPFhLkfVltYpH!I8qjkt+vBNHW5nXeksG50O=EV! zriq&!A_vww1aU!5xD2P@Bqg4XD?yY{Ak-7tEH0u%mzF<4$;M2Ydacp_rfe=ND}gjp zLPlx_i;-YV9GRe)nTnJNIGDJgcfwFp#Aels+;B5lX=5=au*zQ&!73GOT1JKPw483_ zuw^ljE+@dcA=R)WJDE9fK{q*_zJMB*5=~9O^8*E@K{}iT&4XmEgVu=SG7WYFGc3}( z2zq$}BorGJh5>PIP!EW|PDI{7L`o2KWJ$xl zDboLd^h7F~gtdm#7|6uE9#qm?yjx&ot23loA}UESViGEX;IVQd?oy=(HZ-9*Wjwui z!bw(Ct}rQCCicyt?=`qT#UUYAP6C%mOFp1$Dgcs0hVO=G^s@U}% zFs@-Ro^SPCjsJUwZi!`i^mdtW8(8np^ z6X|9iua{=3nPj-oXCW@F$-zp|W9eQjqUdo!KLN?inE75R&{zP+`GK)wt4QB!k z&x-VHd!0ouni8fc%5>FK3DTpr*94VJ$cI75+(Ms!KD9k1RDBb(rAb)y70q5U`MByTL=!2 z%9az%7Pw=8*@dW{K_#y<{nH^YhHWN_c2OFAoU#}!i5b(jOv0EZP=lx7;=NxkY=qcm zuEk{Dod&+b!wlM#uT?=*v_-jgj1rC0v{n=15vvQE9@Hra*&L;kcwjH8(WVxMs8*@+ zpI2ggYL2p)Dt$3fX>qX9;(x4!Wrq!z^~CNsBhxi*3!ZW{c7zB{C3UX{PsG5mP)f1y zKtZFoZjkY17%~&~s}U%olqCqzW!A+25;n0mmEeh6!AVv#h310)A%4MTBD&GzCKPo= zTC#d+O%Wc@V5!r-3wXm9LzxG6;>LlVn_#VxmWicDU$Q4@s`1+x1Q;$3F(y5&>9kK0 zkrK=~m^Y%GHlloX(*u4KG4O$&m&seZziFOe-Nf|Z~FqySSDyS4+NKy7= zMY3uaRFh$>OM`)A2?;+eXQR|9m@u43>7@+8A04D>MHbOH2(4MxO z>Py(uprb(Ni_E}tOpH4yXQUBI5;mZ9mp|5aZaRhqR*nh3oF zZ2h`fo7{N17L9|-1Z|}W9YxU{=)d2j*0hyWD@`6~W|>OHq%+1SM!<>}ph0U(B@Cs& z9>P!a%b~B}sS~axtx7no(DdjaI#&s2r;J!=sv~mbG`x}py_ptFoOYa3H)=Pnb|f+L zSQ=xt`DtY8O;cAdp`U93+cpD(w$0$qUishd?07c%_q8Ic@Bhd8u79Wgk88!s1*_|B zgiOG484CmLL>k2`D@HMfvEM{2D#=VwVUg}eI$LVPDU}o<+zJ&*E6H>T1qf!|e^7`> z9fB&QN;`k55TPq@{AWK^iBuS$(aqWs=>9gXJX2%YipRuZ;X_(XD8qc9*j6g04V>PS zL8f9$PuH}nr;!JPA}tkmn214ae?4K9zt^Cj#xcML6u8T84H8QU(f= z0*b3K7*3#Sfnrg*OqA)q5rZ0+fk9o5(F{d=ydqiEyd`X**)aG)=Y-ICF?e)!vfSLz zDI%0hZ(?vNVG9L%l@^oK$@8`CH|xJ_6Btyo%T$Pl;OQjP?m$bn>*k{^8m89Pzh2)3 zo;3@!l4KvJvlS^&gQO0g+l@oB5*DZA4+ef zFCTQMCW)ZknV7W$;s7q8b4L&tkd@A)GELcWdZHuQvmuno@FC3F#2v(qKU>(bEwR|T(R z%?-0EF%ah%3vM(M0Te1>k+9c1+S_Q5O=-awK4u2B6x^|K&A>A8TmbKI*aWQ!9cTvV z(C8^==n#EcIakoH3znONn;6C||BBwwz;ge)f|G*=Ec1X*FM?@1(+pc7p#KkRZyskw zarJR`SNFZQ@0~jf66_PHm@7om#rOr!mfX zT>LvU{>>|XYFxr;)&XzY!vW{lJ=#FiwE9ifJxx4$4Q^id9xjG|fTBdEIk7LVwlugc z2+os8S6X{yL;TIh??BG-%fY%Ko3BZ4drg)X$P36R2DjcTPA4VrJ&SK^6ZX* z(*qT>GgWJI$pKbPdI%;T#DN<1X$;L?b!cv&>iCY*y;T0eZ3mV&B?e-@9rdg+-3*S2 z9D9w)vFr(2E!v}jiL@%Tm-Vv&^BPU@51kfI2x%ERybx0p!?Bv6@Gw{lvEU?~dSs!- zAruZ!H79xpuls+MhBd#)$>Z>_m)it;Z1y~TvYE%GtUKf}le{*N==Ehj#}JTir#^PT zy|+L0`4dlcsE=k_0T0FHf#~7(`~?d)DNCD0ZHYvsHA$TZ(tA`qYCVvpMXU%g+d!ye zK%$&|n+C=}yZJYE&>r=)S+FGAmy8bYJakvRt;W8G4`ZS8{!bx4_z0P1G-Fzrvy^jVA2hl_n^Pr)svL7TTe(mNGq9s)2T(n? z5zP27D6yhFaXwnE>un#UqhO61ITk%SA=l0t$xn={PfV{*ET~UxMle^O=%kG}oRXNx z?{t0-=63+{I)~ z7h$~R+)~3tW1^FH$Qm_&S;xI+n}f`QOIE#_U7#AnfwVFB_1r>HPDuGeol^dDjSZ> z#TpV7eG^66IvvVZO7BGNphSOsf!>~DJ#Uv`Y!54&CXW1}G5SBbx@9dV7fC}0B4`82 zCXz$svgIAv)H-Cp*m=7pb^$_t*`7i<2_8&!YCntDwva7I9_sRvNQ>RU>c=76mbOGc zcO9JQ%YC=5R>a1cmgpx@TtU1#zJkYaCS=T(^Ng6Jvumcep5$}uS)EQvG-`XKP*YiZ zm(VG_p0>PN;M0D7dz|2kIUFSD7tHthNhgufVgwEUXMVcr{QP>G=#h9U2g4 zuj7l+poEUKNn4%zZRbKqnZZxUoLZ!*PJDEtvz~)C^e(9TF?UXn2ejpEPijT)RU|bHy`@cLo5hIi`dRZCiJi8u+#mgYK^Q+z8-D&-tB9j;rEb(1xc`wt#@i9`m zw}X_!eQ(xk^yU5dT|>uY zpS~PJwp{^l#?i?9GR)2FdF8N<=`w3@JDR$&lb#}P;U7b(gXO3}i$bH@`y^WImBES~ z6HU!p;8fsTG|AcDMrq~hb_rqTLKSgcLh}f zO)^9{RsX)QzURK=A48yo9q>6cH6-jGkeEO?pZH14*zqkfeXhn0gCWjv{*HjUi( z)_vHjeV^^C*uO02t|ZZmI+8&L8QCdo)1Z87u}5~5q#E2Xul1oCZCa-tQEC3(OqO_; z*%f+}HgLyT^(tF-`!u)au6bahm(*o9XtmENVqvl(QKKES!FK+}-d2Y-&t$Lw0ln3( zX&*y2?__FxaIdy?g#F!q#pEGa{!~iS1I&y%O@>t2&gbS((L_(X?dgw86wq3Jd41T% zkv6)_T1AxF=)~RH*bCT+D?kNoCAo6~`H{bt6R1q{;$llh+A5^g*}JuqJNojsH6T6W zk&1ofhd1~Io0iaceNl5&4hu3@@46VvN)xc2z z?7IThZX1efH)x?M4{3J=aSv>oGI}S2Eq-(~y`SlPbE3L!P@<+cR3-y*2zsRAe#gb- z>xfG}mN6Y4ruR^rXk}htN(L{TsVy`qCDErjQOC|VMxuVwrzKIR{Y2qDak85Gr7%nP z`&GKOs>F8|%@gSuFGUwTV#H5hvu;0jKJab4Qow^-chz&Vd1xZY>MX0KhxNfutiWFe zBlzDlS%SU5xL=dg#uydFzG_Az-3MfdPp4cZQCl}a9>b6k|<}i!q zc|18C-=5ejFR_enG74MYFaleD<#cQ7*6$9pLj~grPPm?96ZU~6hbCCpH0aa>$A0yA zpZR~#Tb-F1ik*>=0t%pc>5}T$%p=%MU?S!wAhsnAkH2xjD#j{nBx|rL3h}H8p+$h* z&F6(O{8}x^Fb`#qZ(w31Yb%XRgY2TJCkvT|M2aC_yHE|*szJHl2{<}bVx6bFvs>Qx z;K#mgLBrs;UZ6W@q+)y@7rSf@f{44og&NdY9B>=$ zpQvyHl;*Ma$+^0f-Lzt)wZ0({JWHXqj5atn_@W9;lGB=|hCoqeG z#k=_ND#pG0R!E|9R)u=~1uJA(f9pEG2c}RM`~@v>Vuh3_We^nN7PShFLhq z!KCfKoR(q}4G*M72;ShCo80|(IQ`yCTCdWx^uR__5Bv-}1ommd5PJ5b##&8)z_i8+(~$F$7!g z5IbW^CHJSjcuSP-$;*4H<&&CDE0MgU8<-NLXOmXRFbFp+4Pe7i{RTfwj4$76{$y3b zXG@apLGL-XI_#n46CPqa8xy;Dk*JdkR9q%nC8wq}cGZ~n1Wn^UKvK$Fibn5&rs$tN z=&XG2h5b5xzQ%b~{kndp<+&Bpq@any6)=L*8IGp3Ro1<7D#?DthV?&eZdG8ra-ceH zj^AK^dDugDx&KTpgjJNWb|OhJt?E9vGdAa3Q6L{(ISX5m1!_WsSJ;oqm}X+kq#<&Q zKTFH%mV+)s_*4yd`izW5-XZEd!N;rp9z943|9+kon->eQ&1|2R*6VXnTUp33m1mM4 zoQ+9n)uUsbRwnp(xesSRZ~@uji~c{K5jG}+cUEyCK&d@1w;iXJ-{ZzX**)&>vUk%) zA%a&K1j$|f@*+D83z%gv&7phsVk0%+nJ`N(b*ILTL7i^fgvkY#q8DuFc-*&8*VXmO zaccGs=bg)~8_rdV=fmu%rdz2iL{8%&gLIcE z<}(?+^)VfS=Y{q&VD^?v8(}oTUw>>8o>#_w=s(RhyAKz!U*_vuR}l9??v0;3Pd_H{ zA<6JA;S>A>hlw@RJA=d>q0HuN5gwJ;+b;CN5b&Xf4^vP3pMCCwM{sv;@3JDF$st zN;6DBA5bKjD0TA5UXZi*8`3KzGtfqe_%xej`Sl9E$4wtEQ0hT@@MC`1oFb=CrOMiB zWOO?$FTBnR`*L6S8W;ap6{07CJ1~Z#1&H~7OQ=lx)YTdZYClnvMcr~16-DlwH zU&-@gztpD}KL>*kBowPw{>#-jaKC|YE}`TQTETzC%H0Y6k!X=6x|7oJUG~`2w5RWJHOaLmxsjTCU*lR0W z+|3!4gWb)URpYqXfwan9wIASG3ULOZ*DSysVjl^pI#hs+CKJ5s*ZF3}-65LRr3#9F zc6uvfU&*x-9Ln_{sH1^S8@H9bRe{CG86W4XcKD0illJJYMVE0#O- z8|9Q9xqCHFBE|y{@AzF}UQH`smQsl~?V^2SiHF8w_kCS#&l1;l$8ukPs33&J%3?* ztNTUydqfr&E0G55?PzOnjcT)o6~{04{A0@0&%7pwHaJ9)L~WKe`!fpliM>~KyX8!p z)peB_{Z$t9;B^rC@~Oa?eUynmqnHd2>MP0Fgqeim1)e|4QdWabCCKy-wne!Sw3*QF zTrRW7$g5XQho#H!gzy&ORYLg>xu~sr5X!Fy@aWMSCa@8(l$%9_3dODZ6_m?B=M$=a z2Y8vA?E>2XNj`w`xxn*)eF1wD>h2|HrN3uGkrs-OY4`puy7osF`sysMEk@>4|31`v zRHJPnYCXC|!hNXim{!%lVS?v(6`c#MVNr+YkG1ZvVK2`g=ZGj_yj6nNvSjfz&(CG$ zn0%GPWsnQ4UIH5058){v8rO_onupCFepC~0>^e#VXUA#>A0oUY8*vo_VlA#C}9PI^zdaA*_)N_4_ z){#Ah`=~(PJLoKU7*0vbppsy4_8Gu*Qc=M|#g1liv1zC6opOYK7}RVg9M4a9IG4gx zxhUj|q0XNPj}~7(-}1jB{;pKgg2+s;*Yg44_qhbpO^^#VK@RVLkO}YOS~LNy(iLCI z&3Lb9K0j~famou+IeICA2yYVT{4n;e|DA9;92`vFDgAqlv_(w+U+Tihdna3 zzd|hg39}Oa*1jrYKfW*2Na$h5$6E)f-urbEAd*=i+0*dtS0RqwjisUbbk{C+f1m!FRm$4aX$y`5M?2A z9sl$qS;CQN&pU!pMekXgRz+8XP9v0Ck9v9G1ZZaCA-%RzylU<>z*Je@jb-z}mm_;A z!RwicYj`;luGZ9$PyTfCCM_lJ^m?|(61VLsLE8p(EV#X0)VN`C58N2FgPPa|eDZ7;|R9C8@mL+eY zkw&5+e~XNw#x~NjYBiES-Pzr;0kXRvpvD^s&hC~CQ*%5Hewd&JI>t6oh|Bm~6dLx} za#`|lDD8yGzmZX2vWRWYtDdU@%u6nZ-en6Qt}DR2Dpvrnw!eg$3*{5_c}jmv5fk0Vqu4)iOXPZqxh-6P6GDCMGj3i^*Ilb}?J zGO2*|0HJDcDAl4|4mw8^>eOP5^>xs5qF}16w%&VZJ#Pe|P80X{sfV+|0oDrdf^jp! z>wPm9#~Eum#}sS>qZXr`Yfj63V!dD_s|b<8{R-LuPxU2F}57_h7CO-ho4f+L|6bl8FrF#Dx63i zqVSCd+-Nv}YbGr1frAe(Z^avj@3i1&!w<=^DBKri7l+G9za)Hv(wBx4&{kRaSUsi{ z?xeKk;TZ0FhR;)yitrKAsSJlxwyN-0O5Q7+geI%Qvq_;Qe1i0A!yMPT@SqYD7reM9&X_l@C6)Uhf2GYV}E*F*0cUeSaz4ZqvSNF4THb=Vpj%F{o*2gw0p zc?UWS7xwW``-zmfJ^X8(=M4&Ph29Ykpd_8)h0q6wFZK1jA>nUJnWKfjrjEnH1xOAL zhoG?$;ZM-U$Z!WW8WnD*rlZ3zLgrNAFD%;>$T;YCPJ3$GmDc?X1VAb(((L4JC8 zIW0aT>_UEKxD?7kVFmZI!hzf$9QLNZv%~K)8#yFA4Q znX|*il76f4rfrpOTr5% z)urJ}#Jw#1U&?<)_%3#}G2G6eab?)V{Z-){%6WD8C>C-}_-Kx~TiC>Pb67@Od@Jll zI@g6G(Ac-bkFoqM;pfO-A09$#H-vx09=;Q9F7v#t;RJO4-EbCma--Mkwh1Z<$S3#5 zfb_gpw}HT;eoU*i>NT|7_!lm%ZY!bHZ68!<`(R*uRYZjv--BJNORL*Ms1>#1FIdyS z&R%`FuPx`&>UI_?zQ9cm9NKGN#mtF0QWxFfA+*QP>SlgNyM2YmgC&SnV4y|*_x-e1 z8Rl&a*8ibj#1ArC${3S4A)B3r=~Bs#B3vpu{Y((&5E4DfA-5ZkU^E{iy`{k9Ix4opg{f;4z-%S zkno>$C}+;1Gx&dTsKaoM!}FhVXs9_7!}OnaXrviNSNH$wP?vdElF#^MHz|1#dE$%Vap8IPT5HbqN`t?GgoEqb$8h7Jhx6=zF!7 za^EO|<@`L)3B1}LiX(#MJdx*wUhQwi5y5sawW`e`#%f55=Q_UXZ82S2y;##7ixXbW zat2%fQ$NtwhGmY-Gy6C^IgfpZ=j3reZ^;j3WOFjTUZQbE|DKfEN0%yt(bG^w`xtv) z6hP1-!sb}))tdg{7G$HPv}Nuy3reD&(2sKeYe9K*r@-eHR7EdK>o7fp5)KVDRYFOJMw&XIltW$FNfbVE zpxgGuT-qf$$;e6PG7e2Phhx;a0w+1u$Vcb0eyLP5-JGc;3mtF1IY+3-p+#ncP_aWx z&80#m4jpII1#_h?mF4CTRjJhnKu0}VTC-GjnYKLwy|07k`ILSLqDOQ@Rb?9#M4$dG~=g_^T zQs&Uxq5I4~@!YvS4&86Mia_-aJ!I}?Ow2Vn^oX<2Pl#-dE%0i(YGLzXfEzG0JFHb^ zcQG4N+*@KUPxQUUTJD>s=brP^GYgr0%ko}u$$O>|SCQN9P-q@dRbO-{GW857xxYD- zH8-n}FF90V{wT?p9V$1il6=LXDs#IeUv;R~L<2y7cc|X%E6LX!YBrxs@^yz=&6`+n z?hS`><{hCo9qP!cAHC(kP&+2({^8Kb>@-#ApAL1I>3Fx?+YWV`*+TC)G|Bu#TK|_r zlg+J+HMw^kn(Ec8WH`(H+YdU?)+nzeS838eqj+Y5G}O!3zU7ox?Lg>ky~boE7n$qT z32Pm1sI&Dtla=!rY4~#~w7%nYIScONc->~3tft=aCONxoaJBV7ib z>6t@jzC&Sl`ULFaD2F2RCX0*Q0*A7uf=13Qbf_e|k1S}BL*-tLEFibo1hUSilH3xL zzJ;vJ(PfaAIVn1hw1134q4~Xx=U9g#^RXuP$2pYsY8s{e<4v%PRGPxvnI`?DExK}D zZ`>5stJ%LEsk4mv1w1=!{hJ%Bg0#b5(IH`=`%aM+-8S1xIX`_pjiF-67zxLUFUdLm`@e&+YW6a-e51c#i7m4A6)O^ zZ80A!-VKho)#wp(?mG_MM7$whZmUB#do_AWllyL9)Vxira`y-6wX9CF6J$LP1kNoP zU%u=?heC6rYWk2v5nY7x|IQ&+7ec>xsKh)f-}A6T<>oZm^dk;cnRnD<{@_rpxlMKZ zqeJy3q+N24I@D~|%kw?vP^%@+XHn%yk;P{_N0DGe|jXb7-WwOLcg{ zp)RvUeMb(@`X!hdmSl3h>ao&r&d$>tBL!_$s8-3(`YCHGf{W|`=;0lnaO%gvu9x!s`^Ud_eo>@Nnve^E(OD5^V22`epENYYp=@@FW>Z5QD#;!wG|ZuLGfJ*wxI;1UPB zgUqdhOC6jPWbP7N<{)PTtY7~xs<7k0sb0;o@`T5RK{M^!l*^qIrhi1AbzS@vr$iyt zvcjRzJfaSGszZ_axiVPkP}Vf6lbz;JiJ2yoUFA@D)-GLEJ5-gOB6qXKp;~jM4Dob_ z>dhT0)LMs{&D%1rGaPDVx~dt>nPCu8&ZhF*xnVj)Ji9;1o$rJ^Jf4@kz=1G3b1GDmKK6Rhe1x-hXqb)?A_Hd&!}C*T-LW@tTd!t>j*DyjC+%uI^Qb za;8n*`|l2Qm|FGf*BlyZs^vdlcW9)UE#L5lLtRD>-Ewa_)NK~aBHnUnlJgY*2uq~r z$>uv~Bll0Iuc@X>9`$X9rkf9i-f?J_xrcdR?q3egaX#f;m&#moky`TKjyK<2tav*d zT4XL%cleJ(OWmmao~!$D=DUjbzT+*=PSl#?1BX_)Is1oBa+P^kW7S8l4r|Q8B1|VFsvGK67Z3S)+FUUxzlEd)0oQ zJG3Rcuk7Uuhqk&^ftRqp_9kS#y|blyqpj`M&y4%AuFdzM6kU4!veL#P8+O4!vc*B`e4{^p1I6qiBIcJ4{Yi zkag$-eFBfwf5Nu=PSZp4p(4loEc3KFcCkaApZTkpd?(KiSAM3STE5i5$j^MBEXy3s z`k6)QJ-nbI;SxV{x(teMMhKSsnKPvEo(@*|nSB(u!ogZU^MuM&nJCu&L_LS2+BGur z+TVk;Mbow%$6#kL56QA)nd3~NlJx9W2;VrcWwZU5uj2?r&Ve>Nlr_U;2YnqXF{PS` z^mC}(%{^KY*4I>-3slBd$E!8_sy_W4s?Q!GSJ~!JbM_#$=KzOV&9k&|F6R(!F4sQL zp$>D8a_0}rsboXV4`sca!4(>5K9bw#yAMKL=1tAR`D2|z-KI@_XRt$)%n@avAr4K> z9!TbV%|U`wO|#mFH%x@4V{oA14$U%;3XO1RjyX%+W28fK%|0@pQ4Y;FePwo|9a>}# zQFdb-T529(Uc;vVY!1hnm2!k*9a^prEPFY=FrnO6nBQ{E@fxAfDzl#&vD=}wrc=&( zfuM8hR_{mb{2GiLwB1#>Hr5i zbgyZX=BGP!pQ(_pXE=1fc|vHWLl2oM#XHEMM@*gK&2s25uV$EL*as(qlj$%`vvQ9n z(zCG=ESeeD<4!7`S^UYN(Dale|Fc7psT11f5R+Q%bw1%xi5PL|*+l$LltKk?(xop-E;Rd6O3$ znrvpssJ1&a)wHU2z39+%uckx(;H5;M$1F|9TpZD$f2R;WZ6^Np^@e^p^QUaX01?xL*3?5^_Q$e zld_YPW}!oq%|;nXkwa5m^YDjRG;~il6IBP^3zWsqGG{4Gj)e=&(f7mg(GJaJ;et)| zaA-bgFdo{WMdmF{G5CCs;w|-R_LE1h;6t?7;;3#v*Op4JW%x0b>N>fUb?ctFRwL)Y zl#LXcO;TUGLy`GV#Tw*L);y>V(cw^uxmvEM)1h+Hw;43pp(^u{QW@ewmHF}Ip#d;>>QeF9vlR3 zV#@A5%{SkcYnYTO)}xU{nKRTY_I8qtzmpVkpH%53RGXvnc7IOyc2QE~HL68wuF#0K zZz}kJwrJXro0dxdmXXBVqv7PhI4QGMnND{oG(T2hXE+p@Me=_$Q+C5NYsQs=4syH_ zbF?Zl%b{{pqXF_@hpJ3Nf6#1)YR%8oP=`2m)SJ(woH;JdX7g8UDtD-pY&CCd1Uby1 zoM~YVkvrU>4s)``nz;@QHFv3GM>sUnoT?3mc@A}%E2Ow1T@Kx5ZV_NP|x!GGfImw|avs^QxlU=-8ujU@9;gnSHCmM=!tWKpL zR5^yKZBBP_;&QCbm*X~dw=*0sF2|YqavZC+S?72q<~WUh>m4e0IiKY^LzP$amm2vDB>Ri#CrD>JrT4b zwtuR_Pag`5enOjem;#-XAU%N~t8)>4#7Fu$vtvOJ@d;0lZ;XqR@G1@=W=FE7hM$UU zkkY9Novx`}%w?DelRGP;+v)PrbQIZHRbMth$+w~7&R#{2wNS)p1eNKmwtzS7u#nCg z3zAV!Oro>af>bmUi|DK?t0&PYx{HSI>}^3N`Z4Ku_OW8w=tBB?XML$uQyA$4Zf8SL z0XdXJIs8{=W3Ng;dGrg~ytApk2~ZVHM!2)NVQ)Zf^l28*w{!uZKKd>ltFvFl34rG4 z0laNzOV!1I)~FIab+-1p5s-_PVQZcJd;J2?5iP(rI@>CK2N)V%jYI7m(CZ1n$mmsx z<$Amb=!zz(1OqGn0qBl6#OHOkS0>TUB(Gusx=Kg=?x$Y0Qr<8TYAcJPBT!hh7tK*w zY^&=zQ7beZ0XC9Z1s%} zmo(lFrB;{pfMP_7;>1o;Vtwd>*}gGll_>lLZ7Sc8I=* z8jFGo-Fp##OoYRA)|-Y=3KJ?ySP*e+PJ=6d@O6pDE2$}@i}vDjB*8w1p~NB(3jC7CAw^3lT3`zArwS}|aI$><>`ySl=ne$6O{o(o za3Z(~N^2M%BYPEP|0q?MmT^Wupk-4<)&g<1%TDl8#nymAG_3vCiZ%>ART8X0xb`M4 z6H`WKvwHePPl9HAr=F9f{){UzoIe1fi&JnqHl>jp9W&l38WSx_J|Ai~x}ZVe|%? ztxQKMbu#KLkCU+=<)!)4Dqdx_tcuhU!DRJ=$!NHw%8x!%28)PNWo$BxsmqV2hr}8D zop!2nBTN{rp-@!`TaIM(y$m3kmMzgk$gL`6IoarMSYlOVL1FYb)>W0ZpeXuT3Xrj& z*h{MjRRzInTc}xkW3k()K~=WgQErwE?^gt+sPI&B;S00fjDL?X;ixK!$J0hpj;0e< zmBy3?QU1h8QB@XG>Y>=9E~{x8>lv1Cwr43Iy@bNmCX2;DExf6aPEvx}DDI7CQG#I? zlX_Gav=%TUh^|48bxs{#G>Y2PIdz!aqN7%Ob*_zqsGR;<=hP8Kui#4RoH`OwPgGdv z)RByeDQ2BhN6M>EMs)?jjVjnw^jY$~I+;Qu2vwz{3ZUFsC^yWDh~uFA3e}Wd7R$Uf z9?CCJd@>OievuS<0Od_4W6E?g;5Me1_DfoPTZ1!mPoTQnC9yfrV_kv7aKn~U?5$m^JxvOa*11NWpKhwo7_FFcxY z1Rp@gCVZ?EFBM)(1Bc-i*mlB8UyRRh?iZK(WSPO|u$xmOd zL+PKf^Pgan>Uxce*A`RkP`4I;);u7lY*jk@i;|1;ItNP8?UeI-1naU>UhP%fb3XMF z3SN6N_v!s;mFD)WWD-F)3Y)&yA)aYTWc~#ucl;>}cmt}?&en3r38XCWy<<&Fkj@U` z0Q6~`%(fkD?*-hZgWnU;t9TV1w-oqFWtd(*h+2v>OAwDn(;h9Ql{U#}2)5NyW`S>~ z9W6a7m8gkkb8RWNAdLP&%eVBjAn6_VUmRc5QdoSEa$hvkYbmlzxxFb}MWPC#SE18v zEmgiUOkYL&v{WbEm~@%cbV?DFnpkmHNKGou9}%`{8tV0aW3{_iH^L)rL+wFtL3e9J)}hN)4T+mTP}@}UV1Q1-SS0xFDtP% zpNr0++gy5h!};r*Uds?ay~v6@mlwGQXsRkywTG7~i^YCyQkTFO zewTrCw4Zc#ccn;A;}Kex@wrU2n+Uc*8vQ!jX-yW5AFQ+aqhWI0M zPxc1q?EQx2D-aw!&_>`L@(QIkxII`wT2jpG;+ z#gs_~4N9Wij>PT7pk2K2RB)E5nIq0Ii4Uk}CctNH^*q*VQ_g`D^&98`zD z88FjtMRJT@yc;m4K(=Bw&K+|&as$R&=V3O^Iw*xZA8@e00m%*}rzl1j#Jm9q1@Dr1 zK@R)|Opk9wdePgLk~hfrr}J`JK^de`S0Yux&2Oi2Q_anU1CgG@&E-F2MdqZ9Z2` zBf*Kw2GUNaCA%Rkwfrr^sOSN}LFaKZlbhieaxx4+EO+> zeOOO#1oFlp%~pBx;*;RybYFw>t0WZOq1vZLE3>iSQG{}Bmb68-3!Ml)oKSko;)PyY z4@-Lwyo1p9ZmOQ;19QGEDq&kqL2wy)zK#*sk8pb7#XGAPS@2LV*bG}XzE{k4HXd|! zOFfb(s3Nf&NqKNTZjN+fey1~xpc~pMn5S5})px*g5}V}JUrI9Tz4f0yO0vrfj*caM z{$+{#zbLkuc6-5cB$DXm(O6;y7J3=9c~{eKH^5ghs&`xNMQv+S=a}L=8os!g<{}hqMns*X;6`p#KyU1~Xt9MgUPSEFf0Kqt<_-;{-wlHPJE6>h;_HIL^jdxp1m0Lf+AUX^q&_V5 zh_$O{M$_uK9+0mwc9d$SgpGu@sihFFb;MB+Mu_+;s@k&I5f6YcmC)x7I#wy^9Q-U@ zqU8ZoBZc6^+O$k;dC=IAX)%&|DXGsRNbej)T1y!MTDF-2dHM_BoI|j$ytX{yh(Cnz zeZp+@s>eoC8(nN|UN-69lE&0i;Ut%Fc?h`&2wt$2y3OzAGavBjux`|s39rVT-Ud^y z?yC&1csofen&oIRA%jJ^^#(6QC+3L)F9)ilwyX?_?ciKLn>hQxx(1dj`2|6lrd@xd zB-;snUZ%3^#Xa&UujPWEO3V*o{D;uvNG7|`l$PFkt0Z{ECy?fC<5i9sW)0VwI3KUS z9Nh<$-PZ*%uca+W6)Xqrk8D8^k<^mKpX`cF#Wm+u0+`^~>Gb!Om;I!^CwVU_^tU`e zjy8~|XnDoAZlQzx+X)B%SXTF{-$OmYY+PFZ)-b2_DP!V_l-hIY-rfjLB-n~TQ{@f^ zA4>4*|4A&BbX`7Hr5Qwm-U_%U=VL)rtc$@H+E`||i*-*vR*e~`SU17FF&_(>V*L_) zuZ=Y{j`emvR)eui!oR?MA|DHyV*LaBrj4~<9IK%#u2-{}sJsl8?-6Xhpea@rxI*;$ zIdQB*^08XXIK}FOJ18Funqut>-dnNy?4)P@Ofi;uErU$8+Wcr3^9T)97)g(X3%!;O zQ=^&a?GSGz1nbEvMf=`JMC>v=)f?K^E50PAI?DKJ6>4)_sCV*(8es+~sfS4B0fMax zG!^Ov@N)#O{s(ca#<7yXLdTj(s>%m&-^<5>rdS!AU_|ih?~Y^5$;aaTHN|Rz+d!~+ zK~t~197a&^0D?a!xU>K-0AsP&=hMKc!`bmUL5P8e5^%guwtDJcYQtg_N1cJ+TESskzJ4!Hw~mTi7g`wQ<70U;vD*-XGrw)xevuJaK+o6zIr#Vfp) zC;Uj#KZ5W>rLppY-lXw;KgnB>(Q!8ZY5i4(tN0k2$~NbelQH_Md^@tvv99$hzf;v- zRe%axzU2xvZC{z@xBPN(_LbA*-a24!?5e@Yt@4xljQDh$#bMnP(I&jgZ~PPN`zgzP zgb6l-Repz(c^$$lA}(I8_~SJ5%9Jyf5Zd;mYAt8_c1OJv!T=Fh41~DO5p`JbXhMUY z#k5_~2{Z)#BqT zc%z{Te}a0yj5}&SI1^(ReR76aIzNnDcg?Q_r+8POXq4*wlEpZ$^;WmLjJg8O3D zUU^n+oK^9kVvbExR2#t?c_dZuJ1?m}-Lc+QJ+Zz&K6OS?f2(A=uO|50BtES0KUdT5 z5%Rkeu$KgXAcy4R5O<)0cL@XX5`o0z>HHH?6;>ogQ}HKP_-B)ur#}-iN69P=N+!n1 z^oCzcu*pawp$m4hJDFjyhwM&fk&;=iWR?YoJo&Yr2|aKKk9tjt4snaNHjk?k1j zlb%Z{q#(-d&Ae1D&TJ)lI}zTVw;}~-+?HofKC|h&GOLYcI`cB~^O@D}ky&djb#Pva zFD}`d$v9<4ffo#pWme{8A}8aIK*T@L3&zC)*X0GK#)(9ayF}2>ezDB2^D^woC?l<{ zoV^yy9#nB#Bfj1ArE+|MpQyw^NBboG5x13a)5T6fXFqO+CiM|oNZ4S>-gMhpz@~k{ ziHmaZ>yoeWhknkr{LkPQK1e%9b-lGDI+F%oMX+n`sIFC>SqImDObC*+R|y4LMkMtI zzK>-4!+N$}V%FI)>r}Crs|M9^8-L$164^zeu@yND3Dkxmt;d>kTj z1%Llq&Z(I(%W7$t5~=H1P1MRH^+%dE*}A4tfvc;rb$yLGZQoT_W9!;f#cG^TKNl4> z6YY?FILol;s1;t6snD}%v++bP_?$GQh*M$Od}9m1H*}*5qKdmQ&)r?BpdFst^!7Z@ z)it)P(J}YQJa=lWtlg%503y=Fj=ad8S*KKLalfjJUjJ!eV`2w?Fc2M-UCdD4yrWX< zP%klp-IV6jlloHja_hDpnvC}!FZs208gT;ZIE+x&}UAH8znLi>$)%G?EWTk?-piWX!`zHeX-kmL_w8P4KiE@i;tc zz&4S5%1U`~e}q?j18hy2#Ih5nVC6X_EKq#%8py1o`FD2tF_1|Gw{}j^XB9b&P+5M$ z8m;-MVhunGm}DPDXt)Aq=`yeRX*(IY5z=*#isJae%Jn5)&<0UG^EOB1DHxBkyyDrAm95QE z=}>F0S}(RVpPZBy^@q&RI{jLEb#||;`Q#RDI=w)_pCQcIKea@n(;)3lz#e81qj_2KUYt)o*x#bk7yJfz979)X8#i(ql+ixCvpZ@k zW|sL)^BB9|zJX#+wQ<{-j8vX@>Iyjhv<})7OH12b zujU07RMirJ5Nsk{*~bn_Bpt=OReDC|OeEMEOO%oCZiziMpNj8CqC4i=35P#9ZYpo& z6BuUmh~yeXSYT82YHeCH1@mNy@K7-7gi z`zwVB+KKmPmM&zD+!WfDxDxJV1PqW5J1frJpu0pl#%oGi%HyDi30~rJHmVvcleI0l zciT%3+Mk~GEU`YNG^EkkRwCOD(BT(~VP_1jl-b{bP~&02UPQ37ie^L_33kRHi9nY~ zRm)F2#=ss$&<+MUYJ1mdlai#ED>jxU>)A-DSDY={Y3u#HtLfX2_|_xrvfx?eb|=J{ zBr~0GdR`(_R{kcYIASk@wS>_B1wf7jd|fE9=YK1e=>Iy@4pDWn)az0huC3fi(d(66 zdC-S4*vhRZtuqO>a*_yi!Ps_J?poMa6R>LWwCCoHco&^D=xfS1>85A$t$z8Qrv1$! zdP36Bvrcn)fBF#ZQ8)DL&{DvkQM!D&*HB?6@3)caElRnovK5Jp?X$jiPs^Gtp+QYl zrpGD`nRe@bM}D3IB$8Wq<#*V-$TavaZlbs!dM!_~QxMIy?QS1kbOSYt>?CP*#B6r5 znInHORQ-Ud^VC?NZdxo*b;qAbder#*eN(?`s{czZn8A|jfcA~PW#4u#8wwEk)1hxI%S@j$iDyn`;6>2#e$FK1v~PB z{H+W=zmDJ+U#wfl>^&CzATOB93r2jY$O>k;@q&BfQWYOy>mHY?dlChHEKXyHV{pmw_PLtHZL$M&S>(4W@W^W7nH|BZ{&rh$3oqcozN778e^e?1LM}7lNTE6 zgvKJ2Lr7DDeoiQ_mhsJAYIMxn)06@!%uBq_0NbZ`@}1`V7WEdQm%m7_>(j@cwR{kJ z|1F`To36^|up(a<^{zgH5?3OeoCf1rB=j9Pui!mWQW{?Mb4sMr`qrCvB|l&~EqE93 zii;TT;-b3Aq@7(eqx?iw3QxCQl%fBixHcnw`oWjO$6d;A!2&kI3I{{~f zK7Vz_7GQpy{^MWg4)%g$;<#^riE9f_?UuuR^k8P1j*Bf6a4I8uWdp6aiW@!V49@08 zHEgGCWdp7itp>^9l9=_?ZDjx_s9uYfaB7}p+|4W>EiC-jmAt)cRZ9?cO#_l z9VtV9f+3}Jq&+&hqbk4ob~}>AJAva?+RU&buX=tdl0&T|pEQXlAvvfb`Z*c#sE{=-%dFynfLc3G?XwPGG{1tbB4i? zbkwTgAyQL1Ji*XpxRk%kawM?qp)!G-9lUH7J;&L>tJiP(!bl|j^CUH_UZ8HbRHDC? z;&1=q1?u^m23~XrY5QBI(o8`YQ2yu_#HA^AjbTCe-gv8hV(v3vl(I+wV)IPQ60N?F}a z(~9A7-0yvzJH#s(4L5r#CO3|okCtO{GT_DZfJdCXW_7&$6v@C}&XY9mlJvZozB8ud z_C-(e&E9B#lE7ktUgB9su(IjN>1YqvLk|qKs>!HL{VQA@4~&OAJJ1aFYZYXs{e^1wO~i3hsu=|nz5^b?88iImlo z7NPhSq;@OoYfj{D%3__!Z@7^Y`98Vtb|UfAJ9q@HtoFgLb7|ry;mT@X`8pTF;6uc= z1Ydk3mluj`zIAi=%-8k@;_is^9rJZA`SSj?&G*o+bBP@WmBDDypUiW>Y09o{lQ+WPM1;}^EjyU?xuXBkzI*!}x2wQQA7h7_ExiKk@KKdI4 zNa27ug*i?j_WO2#Ksj^b=qta@CGLVa?xrts^(2l3EE@rn2ozT>ea_ zUEQsZQ-1dwxfjJLXR-Xbum5fhS=7OUW{qDq3Ry$gbVHb{;6H_`(GeXynT=P{a_@2Q;Xz0JK4Is zSG@JLMM>4&3%m}0>GirECsZ;t>8nK6Bb5y8V?9!@|7PLvw}n-FJ1;M3wvYJBG; z?&mzlB<2+*Rmm%`-I1i{Z#e^T>l90>S|rj&AMZ!7eyha2_csl^KcQhKPKGr^NwxJ` zOCilC6x>H@N{8~@gR3fGb)Z#{^;;$G0kA7YVvH*Ve^AZq9VNRPan06mZC(IDyM=c^ z@_8A+>)kWR&Bym>!|*#~ay?-{ULuefY3$xpya)DA33gBMmr6tz%}aOfDgH)OT`bk@ zDJsDSAeoz$Wkv9v`EizylGYyxHcLr_y4Z0fzm53{?3V~O%Rj>Pb@@`3e-c%f-B|`R zf!fD=XudUAo}+s9DPNFzsU3@VkF6KPLjTAM@d?{*3@^yMul+G6bV4kYKFX#PZ+mD2 zc6{EgvN`pS`EKF6>se!Pe$1ch_zBLCy2p`|b|rCTEOKIAWaQUGcuU*na%Enm>uVxE zKt#)q+wvmWScJZ>mXtcS<$2UaHMP$~?S3R5HMHxj|Ll^8w^8z){Eb+k3?sq?Xp!#b zZu~u9>p?z<^BC#`oT6Cs%->Z{PZKaoF(;oytt*9zcFPpSGH2vv_%j$PDG9C5CzPs1 zNL97vsLu;@)BcH14JH;QTPaMHhs2f_NvJT3d*~hZQy=~qWBQPg>Wa&26LBr?n$)X4 zun~7Kq)=~;-P^#^JDbq&7t&uRSHl@vEY9c#ya}OvNg=eo>@k>WP4r5n`miz zd6@->7d%B$I~VatXrV26bdr~Tr#byuUoZGu%vrLV^WWz57jWK*IcGXf;v_HPBwO%N zOx_$vSj`_3YEFMn1dm!Nle>0vK5S0^FX!PhF-P@X%^%mHCW$B8C}z(jFMmZpvz1iT zA+|z2`>2wfYb+>%`-79H0PiR7;%B@4|b{G6JDt9G9dba#-UTBZX)!hv98C2j^&q$!LQu{FOz54u| zSM@H`3(s|>_GIGfCZ1puyy_{P5DV>A?8}H-Z@NXxezK=e9^^A|%|QFbJE z^zot{wms^WV7Sk7*+guAIl{I31(y>)`6k5Q66t>wG5DMqs^XonY}0hDa9e1qqCGZ} zB>_o1otNNsi*DPos!Q@R<;bXC{pTw(8lii~@ym~nb6{6P@lC`Z{bpmHlNXF?HjB}D z5&k^0MBFQy`Amk#QuFgt9kEol#FoevcXBLsc3!H>N!d0t{Nc)AK`in8yhLmH?kYRq zygU~9LtZ53L`-xZrHTD_Eb~rYCd|v!+j=ecg7agMLR5}&-Hgpd=h`$b;~~jS2%P1Z zc2RV5L_0MlAEHrmw2r?;OgsM9HdSkIevw9dju1SBh?s}F*d&cNfTmVB&CxV=SvArM z1*|8mm3qB?#!RG^zgH{cu;YGhrb$U3dJBXptC;1~X6*q1ZyIlM)K-_t`4!(m{O6Xs zYPfYM4Mgir)%!H*^%S;OQ7O;2uTJ_OkWTZI!4mvn%PkL(L^KL^}a|3}MKn zHmCWbnT_6<(R8qyOYIuZY+O>R!<03b*#i<+A$>U^c$WgLK8C6Po>esNjE9cJ@XpW@ zub6F~sLyeGC}Ecb)eBSPa*nOuU8IJCr2T%hEML8I`l)*NklCFCSG{wFsEE&i|00^L z-Z_&*`wYBOv@)COL83ME#U&8N$YIo+`{nwaXX~T$v^pIdJQUaG67t_|d1|vP-xTYWMG)Mt#X&%JeC z^rWqiYVYbZre5t+(+^CrhmvZ>43yr-f=7w24^PyL86(;P@I1-cwiq)-Cn+ufpF?m4 zHr7_)r{M1s4qtr2BEHn_4_66ChU_RDYv<}U5IR zNw&~wPMKG-dF%xf^J+59<9@l3bKwwuU1DPLuMq!88jlc?j{;ue=0$>r68*`RL@}Vcck&U^ zZ?_f_o=CA59kmc@jqX1|;5~vpas zi>2;9!*qb?&nnAF!QYkT<5#l@L0b1KOPvgtM4$`pxBCne$IiXK5^&^2@U-qJkdU4I zCw@&c^-3qZ*uf-CswjT>re>%aYjhtG^&LWaAEx@f3M@?@c^yXxP9~b#WhRl(Vj{ad zf0~3dKLsn{+GW;yZlv|q-y{}X1Xt#_#jt^m3M6^rAQ=mO3{{AE)^bHZ-P#4G~pUUyRs?Fg;BG^-8%>!wH z4GGv4!D5KE1j}06BoXKmDQ5nN9IqRA*AP(HXQZk}w`UREXnM*DWF*|^qQr*{Ub8j}3M$7nVx^|ySRNqxgs6j6ROUhcoyprs^72Sk1N+KFH4Mbt5=!w%F=-KS0m18Y zFBv>z<&RwDRSj!V1buCIHKAN{IcPd7sc*jgNVJaKv?sui6FB!sDs_~%^hl*Ls^1AX z$&cauM=@+Fjy48=J%Zp;akM4ig#@op30-OksZ6=BjC^MoDiyC!2h`QO(mu3O)N7Hy zT+EZSHAu%SbO*3#zmXZx0iUUkW8zSQ&A$TAR6FT zWLh^LC}Z{#_V3vDOzvd6myQcx56SEU=&GLfj~X@&qO^o*1FaS+?CU#IYzZqysf1H( z$=p2&*5ceNPfPMe>1eP`@Dvgp{1k9FHyEyt;n*|#&qLFh&;1;obGF{#*PzYYQ;w%> z9O#bjjcNFOJ$YmMORr8=o|5ePbpyZC?52XR4eNng3RNCpXWm}dJ2X}0XgeS9m4@Sv zu`?tuaX;HWm37I_*cB&pDz8&Ah4$~ya)?K1HBg#U-Ow}FnU zNY8|;yQP*a*|M?aUt}YLZ4B5(YRk543|LZ2mW(8~x?8egz>R(_f&P$M{$UJwz+*fN z>oC9&hB&lC7(xgk4ohYo!c17g5W=#Aon>bU%Mvo-5bu0s!mzV5d=mouJa1LqTel^$ zXV3Y*KIiTFd7r9!>%Z!~b?dIY5d&)Zu32DQAT1VW7}Tqv$TkSf=T#RWHq-@PU{Iya z#&7QR#zF>G511~wQd_iIv?#bSthiDnIY3GH64j#Ba@OVV5b=Kpl|By$nQGZp=s=6K z_N5P=2?_V7UbypCaBjtkFck)2X*j7C&6ktTm%)88NY(y#6yBn6bT)$TmEhM8p{SIM1U*K@5=Fi##e4Eyl^hE+HmY%Ez1Fy(8c${Lnbx*Z_;N`zUykUXwL`QLR zXK`F){SeqWx6h^TD&FDNjx6m0ttR7_fQ9(S32ouxNCdB8$PoEMFlG2GXx3vHml8NA zIrn1&5GLM~=Rl|Tj#y|Ql`RL05K|PA{0G#-+=}Qa1kinI05vSb{1fmB;ZqT6?utk& zJY|??+ZhANFi%8;e}Q5C1lYW%2LZ|JK*P{O{1VaPgo{5w&uT_HnL9shzx7z z8P(Wo{~b5H!zG}FR?yiRWXmg)QFGGYtF1yk|H!wBp)2rT!XHb9!DpKG_T8#XQ*lKjHpIP~;5+ zV;26cmM)xKi>fTdhtjLD%`nTxcMax0Tg+@3m4r{}KQw$w??1&5;S0Z8ijLnJJqrk1 zSLoEDTd^*nuwy5s(89$@)Yp3$p~|m`KI_4*B#favSQ}vNsFLzs(WgCFA7KpT!CC>k zt_-l2=o237n}jix2fGF^EQg_(kDm5m&l1K^9&8t2zrsGLd}s6#5B4{NF_Z^GH5C@2 z$(1)nAM{}AtAH_-2RjYeyGsFUjGpvh7Zb)%9xMkK_K~VQ9zEf~+6ZGP0}C|)#cfV` zL-bA$HAxf$Sroc|;nf(0%Il*?Jk)oHVjzoZ1nRqJ7Ugx(10L$niDDp&Y5?knc|h%m zj(eyNiDDpw3KzEkc91KStD`r0usKHnV<->S4A>VKE6cBn_Ia=x!Whbf?F8(#C4gNS z-RHr+LKs7NFqX#k7%|JYNB4TLI|*Yb55|nfS)W%#lOF8%31cV^#!`E71z_8vtsd-6 z!WhbfZ3pbf7=X*SMw>j?uLxr(4|WA$M_9VG(H$Oa#chBwln2`e*m1VpEzxZrtdTH= z@?cv5y8)wW`Q~Vi2OB1gp*&bEVBf}oP`)X;!Gk?O7(;omEr9(q2AJ}i=voi^8y}%7bBrRrtMCfNh8tc(BI_V<-=X zOcqj8S-w8{`9hoMe;|yZJQzy0@ZT;1Y+dwI5B3RR4CTSFq$>Og^cv-tML+RiOYZ=T zp*$FROX0ugNO@`WBM-KNFoyDAD6+z(C4gNL{m_H$Cyb#y7|NoMYnJl0(f2*rNx~S) zgJJ9_e2hKo;wW^zrg;B^FoyDAsDQ%fvEnFS6Mf5ry-OHFd9XErUBMQzI{JnOiyQ@v zp*$E$ws0e)bi+~9;%io2C^tleTP|7714)1)IOpZ$f7v& zsTDw-AHCm09V3c?EQ%A~zpMmmS@d2H^%PMIWKo>=aNbjuFOA|82&QekNfZNF6i51Y zO1~s})IVewMroAva z>Y)aSVjzpE1L~R!fGUsTk_IFFNun6YqILkafuqoZXpe__jwlAQsH=fmR0!1k==C1z z9ikY>qOJmJaS>4GMY}wdx(g@ z=5b^%j#hf8E}|I7qBxt}MY)Qi%RJN(q8P}cI7^(*JVvAC9_lfo7|5bHNh~S=sxVsS zpAK@kI46q;$_7*bqtBKldmJyyT`>p&F{b)PJM3um9x zy1WpYW9@%IXIgMw^vnXhtsCNMx%NNOrz3g{J}55u6uyimyI^^A`+U6rB^bW<5ZMJM z4>wLBet#Wq-?>PG%ae@{Oy}YW{XTGc!0(S|Ou@iCI2T<(n?!)45xr3N>L*2DtK$3&KXxt{C>|>0E@J2QC}cYXL4E8c4d!4EJ1s zYu`ItWw=qaPQO9Q+j&TLgGqPQbS~1ZHDT)mTzPR1Ve3uU8>e#-w$+6Fn&zU|%_(1- zWnB3rS$=Fr0Ny{k(Zui<`2h2_K+&j)0on4S7bb^~A7ct$uqaxAa>p=;9=rJ)sKT|T zl9s@s!+HN2_ z(l!A2&;VBF1LVx$CROwc0K5URSf^i5{6*6$Nd5nE{0l`N1_Vx&FW*azJYk}o(ovc& zA|h|Yb5O&DG=?LxVs>0MP=o=wQZCM>4@~YOKA&^|~&R4bGgyRj6id+vY$MPG2^YWvWNgNFZz-FI4 zI-SjP9dqwB{OLili<$=s+M*W%vQEmO0ujwQ9ii`P+W!F({5@#SDe;rzE3?t)slJ|b zSVA}PnQ%4S>p?4DWS)`Vb~%!qf)~FnG#7VYnzs~^MF-W?9jmL+6!Bd249dFje-$FV zozVhd(3McZEFmq$&b5@{0HT-xX(@JYrtbE);Qmee@jP|W&PF~LeG%>#Kr2rhDVjx! zzlYb~f#z2FQpk}Pwf*OF5V%2KHT#!HX}+|9hPAY>lDcoWVl`xfsQd`?(sz+c(Y9!a z{Y7K9tzvu8*lk;gm2==l+gfN}pB)*;0aAIohew_$siLZAh;y$FU3IY^x=Jqn)1j+y zdw>lcGof!^iO_iah1zo+dd($%=r!9V?YjlkPP#i=tt0rS-MY zHn``*&A{;mJQrDqfLAlHN~f`HimZB&Fwyed*kK-v8WR=Dso zgq;(`b3d~QIEWk;mBC3qx;dWGt3o~kENhLfLa+*i!UJfaRwf_}imj|o%KY~r`tO1O zubT__*65EAOH$x`uDr6=z_$tfbwFPMiPY}_8R8!WEZbfr`NNSboa``Cm+dT?`q@GoVIun3=Og+J(U*aXO7ukQ=r>%(=u?PhH%LdnK~9Anf;-FTDOE6f zM(f9pcfeKI;%F^k7$H!q-~5)1eeoK`{@aM6rmcc!x#WN+a_0Pc4mryy*)gPc6r{F4RSnK1`Z>hk z1GOqU3HOBr&Hr2c&AH@q+EpRXf6v|&WfPn)ak3%K`3joU>`fOC^81MIyNt8&xlFMH~gnt5UJP!hw zY+gsdP)Ggf)CgR16)vt4;?vX;eY5@+Y;dbV=Z#mZlG^#a4{89;KJkjFlC9$PRX7iW zRw~`GO13TJD0)A4%sli{vuP6%Y<>7mbX!hi~L%mL-eI_5C-Cc_C-XqBRuj z`w02RAi4XfWKk_+#D%R2*RQJcMpen;dGt99*BE{9ZIF^Bvw42;6*!-#*Cf1_ihvau zXXk@fo}H8 z6|06}TPd+EeIJD|3$I?y`!@WCU@g}n-=Tj&G_=a#pce?uhA?*!^IcQMoFxalHf~Jat?}KVg509q0!=q_^yCQh=1Zc zSsYO}{Ld2bag-{;(@y(B5D=~yCrfq1sd9z|kGDf<-e^&^@G|d!7d{g?Ka#1!2oU}* zY&TY5wh8|=EO%AH^78qSU3_dkNKr((g?#XRljpqbAv|v13@@A_Tlf!gJaPp~H=i_E z(uJRgSBQU%p}0PxfiJ@YNuZq_Cy+BKy~6TG;FXc(@4$m8sD&)mpPDE_r+$a2w!w?3 zvLZh6STB&Z$*|Z4KJi%Zk@ffR3MDT7M;aY!;Tf2}@DS=Zf%v{g{FOm)e67UDD8+qh zEf8|V@d(=SA<)G<{K%NNNP8VqC$xdc)Axyc9$vo#%x{CDufXvNEHBaVBRKvaSpET2 z{T>{+uw2as$vnYm!tY~3%IDlIf|O6L|Fjh3iJDz+)Ngv^*ei^C15D(pdKnuzSH;!}LX7rcHTOX&gMRSJXnj z_Sgy+8WIZeZOVHOyb7;?6BK<24%|4V)_|%Az7AmdD@F*sDfxyC^2b2@kqz>fu>26D zmJa~WLQxC(%IB+at&^nMzJu6#8N~a5y~A*POB)|LQL{l+1pkOh@elJ4Uk%-AgWL+l zA&>;Q50;Z4RrH=o3IzqN1NOv7ly||bnu81IB+;{sVHjS2hycIK7!JXqpmqKPjw*sT z04#q@6!*kDV}slX)IN{|IRHx*q?T_3UgwXu(Cme42fRb4E_wveCV2fDz`nt7NjP4F zF!2?W+f0ER~=u#VG>s2@)0+JxRVQB-Y<;T3F_>%pbaOqk*&Tv117jFRXVYv6; z_zo<0gQV8pV^aJxwRQwST3OLj!Sk<1`GVB)pL$7f%hiha`B}1|Q+OOxg8`OiXp>0(29#Ljzn&PgN^v-NR)MhzH?Cl+G=4n8sjR}eJl8j zkiZ%yAT5=Vv!$+K#IPh)>FogNCd;kl3Aj+bknDX*wgg_UA?+6_%qlpR?!fL96s>}z z2bO)H>g{lR9hSR5(%()Znh^h_zg0&fFT&2gpF^w=wRB2tTzye-Y?c0gj=b&l8X)X; zKhO}ZEiu}Km}+_l>@}1}GD2A-Ba}ljq83v3@IFuw0`ea`1u5Tw*Q0Ph42pgT$D6Rc zPRA#3{4ZF34yyhfjwN+CWCfC9W;EfwEaodBk>a^p_D;lTiy7xo^vJaY%r#6##9|U6 z7Na*}@kQ-Qpw20Z^{Ypb_)p<=Inufew1WI?;Hz<@hgY~?vxp?P2~cD&1we%F7y-QZ zSgk&^2LKk`KhFq&MtIo7Q1RP33tkLXXL`WIaOI2u#BkKaurD8A^{V#(z!r4Ji~y)d zA2czX&IefS>Z2xxe?KE&t6KgP030eK&zh({%tu1Q_X2nq838DZcT5a7NkF+80QF!eF_QxiiV2g2Nn{I3I- z1@RLdT%Xm%ov}5>AE9{rqnyE9$lFb@kTLD=;P0UQ?SRY2b@>3>{;+3| z7y_Za_E!!r3!+B{_u5~D@ei~=>QFxhqg+9lQ{P1pxdH5V>o6ODMB)4@EH8u9E}oYB z498tKpN3BD;vvzqP{Yj==RzJG9>&q!W^vx|7{=~DfY(1E=>Gs!k$)6?`C}NIDy{;m zhp13uH)T0N{vm8B7T1?a;w$R0J_3nYJ7H-6K{Z40X@c(~xKV>|0dK^DAA#i|0WUm) z1b7?AY?18t#~|6if!9mmzDUGEzVtequfhu8I{;k#5Afo;pKsIRQm*1Bh1K#nL^%ns zzeBjc0j(haLhx0h06;w5wut2oI9(6AEA$8;Gs|CkQM7*sl@&=*%#*j#Ox;eBLgLv~M)%Q!}ISyv0RN z81!GwjK-s?RA>|9#u?zqQ6ulqX9n9BgCi#mZYj!ZhU`O|CNB^dod(WmEajy*sGdT6 zCHXP1Zla8`jAI`t$g=Ns_)9e{rubn&<0 z2z6XV*pC4V@sG_LcLKK(7$tz8J$Ol?U&uR@6>eItkof)+;#*CxVtC148N*gY)k5B3 zj}J{4m2~-c5!E7iy^m<#2JI&QVer-3LM+xlj($BvHon{eKVI97R{35WvctI!j?k$# zL~#|pa8MgN1pHA>d{v<69oQKidp7gAaNpIqqPZI1u#`_>52OSSR0M~t3nm>iqsvm^oJMex0ba&`9Tsu@3BOgHI^2h96j1S9v0Jx;_ zGN3PkB#pm;<-dY@nTDF%gi!nd4dj%iqusYb<`W@)^6gB_zS?;lGWV>97oLSxkyX5r z1&qH%hzTEp+Y3N10kLK9Y=?`v$5VK|7g4$be*99LD*Ya!l+Q}O1PfK*rSHMA@7u6^ z4$F1u!lhxLd0R7%rXx%$7B5ofu`+faMJ!0nW1B8l6vi0lN-g~)fA-~CX zVvAn8V+ckp7Z1S;x`rtl&=AC6-UCDdG88&B!8|m;YagV$4s`KugZn-*$uCnPC&2Oh znx)Ue`62V(mkL7#4%&3tyuKcIkYZJZ-v}*#^!(=TiN1r~6aAIh!{hyxLz9)A?bo#L z-qXI4`l0jGyl7`MQg(jX>aqoejQYF~Y^C%$FEl?oZ!zi=Z>N=B1uvAxA{8myR(eJ0 z*3y;*cuQf8qETo7XyDNWp-5S2p@xLOE1H7`+%`ll^n18O(vN~3vjQHFp(GS)S-`NR zD-r$WfXto?37FCxytw71A6^0t$%fF?ORd+%OIsFD=p9RbvwkTfm|N&19$x(K>XDmy z@bjb4e&-=tPe?{PKj5K33zmnf7j!P(TY7Ej9>`Q)h;PK3I2Y26El+8v6DcgNAqTPG z6OSHbX{m&!RLdAqGIX}Sl)~!x=zsnzA@0O&=oM`nY;2i>@Rf07i{cM4{MwFkF}#_k^1>Lk0P@ZY9UqU>iVi zK+yd^P|G;HVk%P-F_q2=_8O%f8#X|uK(*l5yN|+f5vV1d7BnGxz*`zDK;6mm>O61I zJKsbvf}zg!7|k&R#bZi%#kBs+YP-NT0wiPG*GkY@dqQg%Yb%>b)h(Xq5z zj`1#UV8htKW>dXDMzV{g&QX19{W;~5{7{7)UN1F8=@##0p45~M1E(O@<@fLPre@3} zwLu!=9B7FG%>qrPDnC99?95iRGj7Lp>!A0>Kr2EfYCOsPQE`(@fX+a*+0BSE-P)j+*IuvzL6LJlHATM7ZDQ=AxTV1LV&B*vY&3RkzS8qXTo*6((qP`MnxQCg z`a=2xN@|B5Q|nB9yOmP!wgINzP!PJRl&#kvtmAfUZglHPsU}^xXq0$DU}{a`#h zr4ku!i&$!I62}fe zPv6%CTMH`~*PcK)Gapm9>+>;AVclTn0ZCDhDf>+0HpQRWpmS;vSVkKUDMBXK?bRJ~ z(PP&|A_ah%3thf8n^_Eic`!2NysFTOm-gVO;YTX?WVmN>S?O z#d<_CbvC@1>rbcqjRfO85h`V48s+$kN9Xyg)p4{>Zvd}F)!)Lk!o*7aO?vetMPTd6 zRCm@bKBh1g92;b$I^b1_6suDq2ffIhD#7^`5yn)>;Xo34p19ROAX3W$PPkw7TA4I# zN%japUcnJ`TfhUkyxpryz4*Mt6G!r@1!XlK4S87*4i9eWxq?rv-BxvXC^*2}1ss>p z)CbVUIPst(#C}SdjvGExxf{1IXx|fv({^)H(%%Se#=004g5N3-c0KrU_k_pH*bs5U zueR$Q7p?qp_aDa9h!*#L7>6))K(Y=0TM$UcgO4~CKjQFpIxoz&d_I1EVk2Bg=V^&S z|BZK#_H|$0Glm`4<&)Wwjj_QkFuNzRQ{x%qKD6o3p$(Jja(w2h`|`2z{?YNF;jygR z+1^=Mm#U0y*jS@M{rVCx!+NtC6w3@v(!d z|Iql@X#Z$djaChf9#B)GlSBJQ`}-<;2fHWKU^9(mOtNWiN#3KeT>i3)}<4W8K;H z-91BBLe4&V05C9qVAJ}(>dkNt4`s8%{p)ez)=>8-+}XjYksg@c6TO31BC!Ko`qz(K zHZZ=SZ@hB-{>t^cCo9)aX8W$(KRN|j*AGk%jqXSI@rnLk5oLYv*eI%EGz;G7*!rHS zq2cThA|4p&KD54nVq$D!^2%yk9(8?vPLZ(GSg76Esfm6RX?JG4yRQ%NXHXC|ri=+< zEp3dUmIk{g2Q&PT?$9W|(Uxki%S?@<#`{&@(Ek2O@u|+FQCO1$V-q6+=t|esi)CPF z6i4J3rXjsoOhzY`q0ynN8tF%29#+kDspgCX5*x9TL$~$^TMvXq>q)e>He8cx#Mf6E z_oiDmW?GUR&1k8^xRwRt^^b4Nj8Ba9_D{0RRJ^*OX=hUHZb%BQMIW{%X+n<^2&t}HaO2_)tM1NmqqQAQ@b6{*Z+r7U(Gt#A` zg9xZ+Y%F{8Q18tO@zuC;;Yikackj(Oiq+82kV-1tEf~u*cDWEEVYQYJCz4nrlSs9D zIMdgUm*gAM&6z#dKv>BFV$9&|@1&w|`^SgIGRXTlYDcnEpS(u$Wb-@Ro5_wzj^hmt zZC!N@D&F5SwZCl)H83`zV;mS}H4jZ@nDzcXm8l;a8APAlHUFyboxovu)EFVCf-&T%cQa>*%2wXQRVnZ z@;3F|>7AM8lv5B*@rJr~lv=vVm8h?qEx7)gM0HhC^-fJpU`WYGMX7jKeP=4(l2Tb@ zwHvKBzB^gXznB_;gi_gK2ho@u5u|O5s_`sNc*DEe$Qo!e$%ckZ8ak8|A-7)yLCG zwWluK&>Y{rGqb6xD$|yd+VK+A?ND0hB+_A{P&*SXyU}2WyANkl^_?;4)76{Nr=851 zM#9e2kW|u^SP0$2!?sNgbl;3#FoZ(PNS{UaT2ggMg)w1&c2LzN>Z@CuuB(>vjwMwm zcnS6ooyZR?2I?HH0@^j zpwm=m8k50l?$r{d+7hTl{>Ma$jY&OfVPu<7z06AQ*wiQ*r>ff%--u{i8X5sX|5gpj zYfUT6bdPgl!vv>TnbOaOV!fGO%nTDK#r>I{AsjDt8bF|F?n$KDGYy@PgY(d!+MT{O zfkasMHA(i%#@(H$L({cm8*6pH?(5I?_hvH(hq8m2@u}fqb+9|zJJ>h2KQqwJZYUYV zD1gy0!^wrp&kO__KCDu8EoQuEi?{4fX^DH$ZTrR!j(R!n%}xxXPUFDXEg_hD_Zs<|iF>b2=HzfZtTtkX9H@O&#}+jf_l6 z_h#c$ov8h-CO{JgJ60q*Ltf9d9U22gOKK1LUth;C zZpl!Y?MkvqC&hUwW1Ax8y^ZmfE)>J~P{vMSnUS%{Ar()hQ*Q9USeTK*NZg#|s5RBA zkT0yzS`%&TRG5!YLwn*ob%GQdJvftT?`mntqz+GJ`$v-fELG;DHi^$$?apf!wjxct z9_b#P8tCrj;$lJ<{+vY zl`A8WEOlVb_V1s-G9%tt*U;S3o>uXuR2)Uu)S5P<8#9JMfw|at7u~@eZ^qIPnMu$eU5u*{C6hktqC^HSnQq? zhcbQG$M}(tvNEbOT$nipHiT8HWK-5jy@NyJ!$|nc)=Rc#YpT3%Q?pUY0*IrsX>S`b zpeAI#2`o=!d512^h3xoXKYGigZt}y!T=q!DI4(+;7#ihL(u~-R2{gxc9XqnPQD>Hg zpa(cp6d2~XgfLCl%o5U1AS24YMR5Uuf}I+l91%B~G1ZVScY$}G#` z&Bre)`G6YI<&=!Nplpt0nCS^14H{+OEH~=X3VBdvJ#VVy`>G2;4j9%jXJux zj2Jp+rW>%@F%WV{kjaS)1GRs6tOpDAUd&cQSZ-k88^;R3G~NFFLl{S~;FX1lv}9QT zViAIhnz3EBv@5oLZnqNGV1S_mGtJ02mTtkmH>&WlXyg^0WD=dolzU7Hz6PB+mA2E7 zZm}5sQq3(*{z60-%M`@I7FUnhvVf~L`E_Pj-7qm)?M}uqJxHmUwHwEzFn>hfhxs7?NIdAESW!6adFrhIcdDwRP^T z&g|}z6=7h)Lb+=P1Qp6?7;4W(Ai>?v%BN zWSDbLRIQpdQl&RYYYB#fq&V+C$qc^A5yY1 z;-QYyW~{-W!Q~KYa&laH{><^HovHRjswEy694H^#1$MzQ_Mr77|xs@h!;cOU5Mqe_DDm~L-|<*gYzRhrE(Iw#z{r@3p?uTt(9Ofj;9uY${Ez&j6UogB% z05hm%^eRtwaH=KU(1uCYEYuoBwSYFPpx$Y0MC-B}otF0eeU4dDvWH|c@l?G^G(d0; zlvP?OkmzhpH7Z&8a_3;QG=UB0X#b%sCwD!JG__J|CBq=3#>mPHH)2f34lIK=L#O)H z$WZUZm|i*Q^^6`vrm*D9P{o5epapA^PKD(-_FfGvhz4{!{MGPp6aO|#ab&Tk80psQ z3U>`;dO;hSnwEv5o39s=!NJZClnQ$uX{Bgd-kvtSS9i=9hAM$@xn%=E35P`*#yJ`> z)tqX?N=9=H@HiM#`5!~4Ti4Vzc+D`AL<#5Zz@lDT~7-78i zf~0vEdtVvixRc}l9&bta4|%gQ zZJ>xaNB3j1i2X|zWn@-RUozjz?mK5OZ&aS(W{~UHJtT)2#NGw9ZkKL7yE7?M6g&czVAjkB_`fU3D$T zx9VoSOX2=sN~|f-sYhK1mV$hEv>`h^(ecVc6rIe`bXxzIYU>{vn>gIoJ&Gq5Bh-Y; zKx&tmegROYXI6>2E<|j*j}jpc_V?f12h~L;;{~6%MOuTXrh1ZQAg1cus^4ToC;ioE z)^3@?EHp6GZ#Vaz1hUOnlT*EXd}CLr+<#&OqtLdUrW)#!8BF9DQRS(H-iRF>o6t{a zL{hy4r0xI@yRa?8%Gq6TTm8dG9jXa~A7Yxq^A8XbHxb+5L z)GM9>W^LRvH^oW4OUIz5*Q_=|G?2|$kefLr)ljedSYXzOnZ>alhP}KwuZ7I3POhVVK_dBxOPN!O~Q$2XtIdK@Oz?-PjpTr{$qegZLJ+KWx5znw* z!ILHqTBCfz=x7DaP$D(&EGp6|ZlhAom~-S2g!Bt+ocZR}#8+Ya&)yw1ZgJ`3X$1bVZH1|~H;ErymQ z@R)E^9UQ_lr=Co#N^3kY0t6OuW_@ehb@q^6zKRamdqTB;C~LMvlBk(au)s>y#p7I5 zm<1jSY@&Yv?m@c=n#3uI$xQcw?xA5Snnfp%c>xc?MTO0!8Y@jSBKpCueneoz<}L&y z(0FES($lfqF5oS_wJySFG{&$ZM0=89aN-bF51hJWoMd^Kmc{IHmqE>?bbh;%c81;& z6dW`tm};Dk$%h%|nq)oCf4np->vwoYeaxp=Swq>PM@!v(L{(w5W~}32c4N(|HEkao z4-d0O_=JZ$Kb>6>!Dy4s$%Q~`kE4$`NGp6pD~AKS0Ng6YVsb4BTxrGIlfp7>HlrytmM>CDTZ%JkWBuW5wn=&hPX zHC3&)B8WX0F{*Q6dZM&}jjdYgG&T-Ri&h|-*x6c5^3G5jSnC5{W6oSqhlVW2S`}Kc ztkJO_(=CV1NY((1f0Ktl2*v!U+Qa9!$f)V*P&ik|T1D{;Gls!xaA^M^)EO9#y~i?0 zOJH$4Xl|hHO*QC;6-HN<#iHJOx;`A#x|>zcC}JMQ*(j^p*eS|pJCF8sWB(`fh*sd( z8aMUFCdTbVb_|O{Zo}*fkr^@l-WgTcFEL8=c_RxR+gB_6#f&DEM9pp;Yrh8%O+_KWN^&-o9Qr2ouzRpe^h4S1UQ7z+Yyv9y zkm7RC)-8ulnN_u>$$E3DX@}j;4>XZL^CMV$01fCY4-v+;so71H=`3O z(V6g;Ke{JqAx-9-@mDWkyh9gKW>V#V26xAvy3dwzybc-V;74V49amvIq$Qgx==rfZ ztA^B2L~7j&o&cxuU_kTTBQGZB?8Je^p&tH4poSLIS!xf80h$M|B#kOxcFbJtde4`1 zUcFNC^>Sw9^73aO<6}5Y(uTD<e;jfz=R9|d;G1bm>yj6lGRYF8XB5`QuN zi4%%M6oTv^Wh&Sh>tR6im|6xG^hXZ(_9?zBzo`Mj4l{x5_oihVp{K^At_~}hIlm@K zs{~F~rOcWMV63$a*!Jfh8fz_smnVHmZ#!fb zR+h=LgSx=`cqG+ZQZ#e6=2ARfzq75O(`#5L-oUa>H-Rr%e|riM^FSU)TC*v60vtEm z$7b`?35`V`>ZwxWcrJs3y2+F-Mj-K26q_f^-2MY@{mj*SjDzWT1Zf}xn2#`u>L=)o zz_u@J*Y;=dj7%MD8SU%GH18`d0_%6o&utj%<9wpxKBf`N>fLG9FJ#0?a44P3+7Mkvf=~4YO z!Rmx;cAe31vs(-ZA2X9seUs7V*wcQ>D!0(5csxpGc3A+TcG!3LtPy|g;nGp5x@T57 z6g0J&<)Jx;HrzeBe~Krxz@!+H|A2`2$~w%TtQcm%*zCI9&>kI&G_$En{`Go|N$%&q=F`QX@En;lQv+E_w^ z2ldw9S+cmpX9K4I*;Y?xP-^2iNNYOcgs0EOGe)S|@r=HGrJC#Axo^9to$2m^GBfBwV5+`e``4{8gM*h|CZxjDE^KT3PY896IF;zb` z>1-{$CA^u@_4_Sf;71$Zv*9~7@}3P%^Xr2-4$6Yw2E7Yfgm=;GuZMQLSF;&(+pO;bHdFBfUg2A1Ff)Nn7B?y9Rb}AItG$=be#C)Hq%+S z*A}!0v;@S9Spk*bcGw>RJpy{%@#oF5XF$(_z6W|9^cu*9<5j+-9iV!U_2>P@q_;s! zaC4*e=RMA(wV-W|{|?wm^`N-p&%3Ml8tTdOPs811cs71sUj2-rvoKx%r{I3t@T@+4WfIe{id6nB^peI0|gKRv!V(T8zNzjKN>(9&PNoAn3%~G!8`@x1wuC5 ziAv-d^dRUF(4(N!Aj>~7xV!LA8Yma_gw;+LA)@CHv-vkU6-Mq7}^KZ`qOTy&+(_- z(oK#(zff&y9qcxq7h!*U1L_KN4rKk8z`hK$0#pfF1zHQT{F!*`z>k48gLZ(@phrNb zK{ni8*atv^pkdHB=n%;AX=j+HL1!HQvsD;7K`(%Q0D2MhI_M41yC54rT8$53gJy%4 zfR=%HQIh4;z5!GN+6KDHkmXn6${^B(pj9C2e;VUWuohMWHPf=+@S0$KiJTQHA+o(7!({Q&ef$nqy@(SATb1I^y5X&Fq* zKMnuaL1plB{U3+l6QJinKbV1!>xhe=0sUYb+62hPcdz6BhU4D_`(DsKP!DJTGzhZc zDz89!I&>lIH6Y8!CA`I!wmJTMUzV2@@qO7dAdB}+t`fF`LMlPyQ?aILaIH|Fa2;!o zHm(FhDoukg@M?x14Q-67UBY+J=mQm~TfTNjjy4;pBj*NU5Pn1?l&DCvSiU`X66 z1el@`6=^zY%mzi&R47LaBM7O^{3Muxu=zDlR%2%F%fp zsMvVjk)w;NDF?)b)iO{7eKAMN22e<)oD|?TKPlnQ5dpGHgMlMz;z-UewZy?S*CEZh zEl11k+Hrf17OunqyCX*nmve~ACdMfB&$r=zKRBKS6{v5WyFEwSSx`uI(D-w)rfBec z8yZ6!Wdmj#jCv3mx8-PS)eIWE{0e!c(FM{N+WLgi!E9*}JFH+ZQR1O+*p$2Rk3-3Z@tL2a>n-gQB4&P$ zZCmFo#dg8`AU%pZpsf^{3tnW)u6rYDGUSxDSGA1F{n_>OrE2ms^*m?*_eSZWgQLQt1 zrmZI~BL+$%y(-lk9^GUUJ`$JCz|jQ4NJ)c37Y4+9L|hO_8bn~EnFiS*kQ5CAAzFSB zYw=QJJ7;pdT>%Y}1~*w4MP_P4~Hc%UHp zKmqOl6*L=r%h;-0)P>^vDSVh|8wyVEcLFvo*(bByi5hd!; zV(UToT2NGJA!(0^JN-aG=K}?_Ge*mYRelBhHzO?LU_+w0f!-=bgF!K}-I23#z#m@T zkF(GL5Hdl77jxuIuDY`v6-Eb*yTqELK{&G+obegwVGW|8<4k)=Iu?#hXFy1uMwJ9g z(@2OlL8A*4QQ5sYTCUgrG?+}Vcd{sC4#p>a3n`djdt9YqlLp3IEQnz z#I@tr94))FC{MBg+Y?OX15jSsT_@OTY{`^Ej)H3$irDRvk20 zAbABE!Yt~*l7x{6OUmknfueL(S2zhW&RyVezCxwb7!hl76b9!QS3py25`wWCdAJlp z8PnJYLN$bPv@l7h?BRlt{Bb1FgV%L5j)6kzS{f(Cnqmh$qpdW+C4e^bllHs|4jm1z z05{R_v$pD95CYK9VY_JK4>6gaFT_4SbSFdK2SN?_LL_}5+;P?9rX6rC#`QFgO6W9= z`^DPHh@R0_8d{=2Q@2w2GVNhh0|NfaQ5X-3HBE!jh16GQJf(eUP(=9RU;o%5u##f) z!8N05J_r>|gCy%1#}Urau}E_wIa-!z#~nFZE(AG)f>fi_fhs8Q;%8GKa1AI5{SwyB zIojjey_;^ELt>dl4*6(CR!=CaJk|j`=}#MfY~D0NB0Xg-!AjgLtUTGvu{smK9jiR% zN-s0u-e$Q+Owg5*JLYi3Ox(%a1&4)_R|;$Xb=+h`LaYq*F^bN*JPJY`(fHRIx?hPZ zhP~PVC^+1rFz&`HpnXTNJ2aXC~KeQ8)Dy{qp9uHxi?`SKw=orax`xe z`;HvVapO-DAC|*MmWnHLv^;DWG*5}WAxD!kF(O?AEd3aKt_ID*R`J1b$roQN^O)#m zP*Hpb}r9TKR)dZ+! zY6^%7)c7OdodV6mezazF$rnYw&>{7@2@zG19p$Rh$quy#rz9?RHTkg{QOQ8%0)(J3 z0>aH^_ZFy1jp6V>41=PCcQ`Swflg){2#vyG!|EJ7ehLb!dJh`$d=YhfqZ>7$zXl=d zMh{=0talzhtisUal!L-*n+K10zSH0}fbI}@jRUv7)8M-R-79ccclxNJzvGtxN(-Xix2MVjr4&3@qgA@9az}*6}zSH1@z6F9bTb<~w_cZ(`j2eSb zSj8Q<^_>RC7x;?TfDrwo4&HiC!_NbjOyJi#ce&2{8bn1dMl~81 z6ddmTn38Txz6MSvU`ddX6slXU-n=?T>ul}Z4kxXQz7QKUIBmm?V3A;@WjgE!G)+X2 zSRf(=rxQ^eP8*S!=uni9>S&Y(HPS1zl}0598>}NaS}00hfl=}ODhS>*D8=*wBj^PI zbMHVQ9 zfMXDk9qt7oMH&x+bgpQ7L^Eid0zt&)94(J&e;SW#|9Fm;r?fwfr?vl%94*gke>Y*K zn-%X48bmN%zn};vXhJ|q8W!ZYEP`$Y=K%qAP-cPxlQ2G-woVoIgOEoWZ;91gZ7>cZ zZWQkh8bk!835EFVhtGL195|E7mw6M(r$0bn;gx=2Xh!cyh)6yo0y^!WXY{7HN_~JQ zQ=fs5B^vFQ(z=JnAZU8K)FD2V5FIphh%Prou z0&5%w*1>-*!8>T^;GJ&pj+`Id+aIBegr(KXpB_B8(8RYXq%k+cyvf*Uj*7iHN0U!m z*x+fBu(Ev(1|PtnQP6lBM8x|reN7hp2=Ba+L_X z3WSt=!D2ZIriIjkQHrpN<*YPewJAq&bW*V#&1R6Z>n+eb7hM2G6hGxEC*@A2e4V6# zy%9{Nj7npP95LQBPik+b`;oT(0tRyqu$ysT8n0HcBxikaQC?W6_|%K#>y|ZDSZ)ck zR6j8onoNrrntW2murzhaS9=W~Daemc#=ZxNs9ArIqvaJ4YLN!h!Sjow^B9~mx!%*V+VP8a6P#}-HaH>rl>UE2NmHs4j!FVVRh`av?lOQT0jhb2hg$ zk>Q8u=<`B|g~x{Yh0tJgf?f%JuYsKDK$J%U>QV?RI zQ3VQuLLr_>;dvA`0xMp@jJ}Z>eHkWk5zdx?kPM9*LBV8L_~m8#P!>Quo5izOc_2mq zCeRFpPZo)VPjjun(lj=UwQC0q9oQ@UP>3gycnT?)yP13=Gh!+DeJE+3K7-&i_=(

Z8c<(C^*f>1(v4qtXR8nx3>xG3$A#yh{uS+${%_nGx~-VaXe}iR@eAd zIHNBsYb}tqAY_Ne4p1;V*s~NCMx#YV#l^E`F$8~{H_h+tN}MbLMbTm&2tN?D?hmM0n^VN_YgbXb=k zDG?>0^JheLY9Ik+pk-eMN<0uEe;#Oohj}1`bPxlWwn5UlfXz-(sj&>=C=&%Sz<4@Z`P|^hjH+f9!14%1IgK06Ql-FMw)6c;9OhAcZ z0JK2P^sm&j;Qob1&|s|2f}3&phz^UGF0v0wx}c!Q1U)Yaq-Zd$=_0=j-!}!6c-w$b zoD{_V?t%AjX#@?%nkTZ4=&*?CBKx4E3kr%%(EXA?iU!k~F7he(J}IEYGX|6=GLMVC z27)&Y#+oOxkLa+7=_31}qzej)?1NGk6jGiJ-Q7Du>tc?uaxz;Bq1NeK#9%aOD~&XW zlMT&b?L7hGn6{pP!3+f^L~nSv(vVAWDu66h+iu0ek=9CaUO^|VJWgx4On8^?w3Sbj zhjQ_?B9jQ1u>)d!#pka>Kx4{K@j!~ z3@xnJp0V9A^Po+L;hEBL5Q^duN`}^Z#CZjsv@#{bWnzrgINgHc$>#ZBA#EB=g2j+< z#t0sGt`LZ~&QOsOc#xb`lk4d%Dr{OjRs9hN>CpHXq|>487n(tX@<3(Q=cJG0aqxUo zFda0AnO;`}y)K|64GZ$?N^fy_toxq?)r$p^E4wfg*Dp$G!b z9oMY7a5k3XM=xC)Qi~Sk7>p_8O*2aU{}#R8_3E-<{^qI)FNtpJ`tj zFKJ&IKh(Z7SiN~&m?GUI-W@cEnBFW18Wm8Ih6VY}BBc0+KnZwZwb5&Kkr{nMA-*+` z&d1K^>mRe+z6@g}h>z}%=4jhzTzBSZI|)M8?uT*8FplPEdsH)QmFNbHbHnhqG2G?E z(CNl-mlK1dkK!r%b)c}S_468<$(O0V4ditjC|%MXYY*(zsRQBfJ>V7r$B~I9&&hK- zpvg1w7$xjcdV2FnJ&(u}F5&T~2g%P65spgngNV;z7cUR@FAC-86$L>HM5A~Hgo!7j z7zF_lRm{)1>t7ghE`cWnDpm)QM{@r|Yal)@`wrDu}JA4`OTTE!LVx z5bwhvN=K7;BZ7UCfh@k!@I#+vLnA@O7#NB*${^>A-C|e{7K4iE^FZRTD4L%xMJ&?p z6ErG7dhBpPw12h)8*+d#qtM_hL>fq2muAr5dqrO!Lxli4XxJD!a()aQZVdhsJf!&M z(jp1#P1F?XISBQqAP7a{EztC|``~-02GL-cAc&@h zY#@U1ghG7jiZ5KDm@o4>Lu6)eJgxz9pGG^+s%Q455IxLUjrd>8hoWQ7`Y1ZHB!Dp>eQbK z@m*2AC+aNYSf+eMbpwcE(5w=OcSaWw%zJ#l^&JpFG-nH_i$)nJf|q)|9!D8p1Li-0 zAR`S1&y$fTfxRKH4jMW{(hU);l9vJcE5RpdFqP@i`k)RM6pV>4^4_dtqQNk3VTpA3 za4Woz2&;p}eITaSk)!Py&7i^HnqdNp!O;5zl%%24@Y=IR4uhdnNHETC3q0`CSE>WfH@w)+Lv^)L*bfWL1+&{gn`2_iv*5=}3#GWagiAR3i3fnk58hnvlGiZB6Gib0FG{cY7 zxB1a0-RP4!MxT{fk~D1e$($d3(v999jimITOm}5ZGP-(+u9L<-5Z+^(m!suQ?e9cv zcaO07A*Ym~NwAaSIa=DaBb%e8OFItb zXwgN8gZ#7%g3y3y41;j;c|0%-mcetLV3IUgmdrw4r6CIh&KF>UMuk{YG)Rc|jvOtF zz-h1rUO2XBQ)gnZ4_^pE{m|erz=T~*hf=o8`T1F@mpYCo3L1r4;KsqzD?a~YytsaB6y_&%YrvDgajQ~1m*eE-4 zwC&Ig8ucKZOWK+>gN9A3Bj=~(zkKLN_rg#`J=(-^g4gj<+eTyGAuzl zXyEJFMoiicXa|V<8zN8HY5q53SdM{>K!@SC`&-v z_Ylq`YztYNd{mznbb`hZNKSjwaso6xM-0&eLi{u=X!`I)Afv%1g2t^N#+J;{cAI9< zurUOOFD)K<4+=Cv<885~X}kl{!qN7wX3*e;FVm&=qkCAO9W-=woo;j;IX}8iH@aZ{ zDAzL*QIdvD)5${US@=IMki2P!;LnATpiwkeiaeX6r4lquECZ#+)4j3GBcLod-hSrt$F%AuojAT7d*Y5~^em zouI+(?DTvS^r?jHpkYCNzV*7Ds;7rQ9FAx{tQAHyo-^LM`b{H%A8))VRuko-`OMk- zZ}PUgCjhz^gqBW&H{{Gxk@!MY8I4}#uP70#y(|Y8DJ;}c+N*N{y-)+6kSLQho(x1; zY^4mVvtYaof_ktS=~;#~EAR;UQhBDGpuxLsbm`IdxMtAcv+`iEh7>QyA4DLxCr^RFzPj?wy@IIp17%1-kvj(oiU zLc5{C6JS9x!|KQIdk^G_X`|qo-g`j^PJ>tC=-{-S(hM4BJ%$ZwO`f4|1R(|*39)w4 z7y#+WXgjJIHW*C?trzbO8blE9H%jP9jK8x1(Iw1_WSp>RIH7a%6Y{zUrXujN!uOO+ zuBV6eMQg?@W@!-eM1z;mAaZ*b4SYx8Gp>DU@I|*^sWUF#VKY6bpNdlVY(eRRSo$zd zf`ZZDE85#C*H&-Zwsn21s(S1CTWZ!$k4&!N^Z8! zi1mIL>k-)N%F2%we!bv$)j$K*d(sm$0A>sydDju%Vslew;btihRnQAXBF($T?IE~vF<^**^sUKrabpB+#DAz zf84ms;m7#1a96?JP*$8Qb8tLHInQxFm*;*N?n;OM0o<+}ALhBw<+*>F=l&?q{j)sx z$9e8g^4!13bN|w~mq145^HaDN!CqHZ+*DGg>d;)gGLelr+^i#Fxv{l*GRt74CDYL~==gc%+td)nG z|LvN0SAlNf*Ot8T!2S+H*=;)ISi|LGZCm1sTbGB( z&{;wu(E2!bu>QDe-3xo(*s{OmjlV=;=u)#~#^vM=!OpW~i!!q)zx{IYcusJVX1HDw ztuM7Qafvt+-3?<% z;&U$M5q*>OGd(`VpSf`_^#Z>P(vm_MCo<=qY*+;;OJa=uL`>H%Q zmd1g8zbnt3$a8n)xv$T2_vE<;^W39(?rfg>);#yoJooWD_q}=U`}5on=eZxvb3dNv zek#v>CeQu7JogKE?w9i1ujaYm$aBAy=YA*8{eGVN!#wv#dG1g0+@I#TKhJX);6bxH z?w91b=jFK<<+&^J+!yA#*XFr5zBA8#BF}v?&;4MY`;k2N={)xndG4q4+|TB@&lw%YXK2P@ zuP>{-tN2*a*Q3V^?=CnI`G%VxDlwXYu`*I~QQ)|#5g^aR~a5I zX*qfFpJmT9C^uC(PYQks%Yr-C+puP-f8#H?JgqNnFPrD(pX!`-g7w?Dm5c8|d@MY@ z4(rNWYrK!wDcrR_U%Q|jl_1K&cQw8z;g{%@bhy22c0Cq+=l~YOH*m&*nW>Cz{`d+K z>&u4WJ4o{oztfh&H|uqz*B<$fnJWulGFy@t=4*Lj4&l8vreV|Ni*>Zyw!$~-n&EcG z7~3vC@_4#k+A`z|hu0%rvxX1fmRE-NK_5jqZJMXyW_s35wN{nmre4eC^DRfFW#y=V zyGHV<<*>5zZBgCErtkUqjwr_@8wcO0ZH3$Pdry|P;LrGMpW{op4+>7&8TVY1(#~WX zR9|ZIK;2p`aMn#7+Bzp5zTJNe?mXSd}7%RwuS3y zTL*80cNFpO{F41JuR}NjzSEw(vf$kahlHog)8?UdeQ;07yBl@^*HBh)EYwjV{fv2J zKj0k=T~a4b{|`S>I+eae&+M z-v|F~xJ})K_vN)W-mJm;vv^*=k&yU;dM?YSYBDYS;Qq*#19g?d;9K`caMNzxyrPA9 zv2BZ&y68R>oZkoGU+LgZz|C;B4cdA>1OJD`-;5~-%Vt|ysjaN>!z9eM(aS5fle~>D zZ?CE<5r*}|JFw1T0*pgPXLJaWOGA3MXY+GnOHfZl&1C_?TRv5noI@!)u(}K8Ek=*d_!-~ns2?BmLp`~`4gW3Z zx~-dh>b|X;;j6B+?uguANjGbT^8A(t%D@-Mncnq| zoBTn?E%|ZWORl5_DlV{F*%Xm3eNa zPaQlA{B(W1>$u6MPF~oC|2A~b)=fTj)7H)WpL5*gQ&;Ws3!o#mZt|(Swr+;6aNOiq zI&Sh`aNOimCk+EXEq?`c$JR|g_0HDK@Y@|X`P4(Z{Ffa!`P42`8*8e@{c)g@~NwK`7b+e@?Uk_jQ?ZDO+NM4Zuko5ovoXEo++|! zhEF(d@~P8y`8|%Cd>*24`S&?)@~H!N`L8=}@~H=R`R_Px@_E9=!$oq zJ8tr++jjY%Lbq+*}lTZDT zbxZj>Zi(M>eO>S?T7%Fl6= zPu+~mKj*l~r+&ueKVIp($$#H*GyW%`Yq5Cpse7?*@_8kPb(2rMi*=K~-EosoJ&en* zfR4qw$)^s+x*7g@$4x$UGA@4*Iuz?BpZXZk%V>|CJh zs>(cmN?XS+VDV4{WEg@LDM&+;QfL*hX_AuArVUB@0L0_Ty(h_~_r-mXL~#T}MqUbv zqYR9LBQG5p%R>pu9}UdFQrzKV$oH*NwJ$hwU-o&uG&qCMZd*h(N)W_^qmHauG)^J zKVh)w?pv%}gCxIrp@Xj4kG=m0gLS`ZL6&~XvNjf7H6csC!eG%=2eR~U8!Wo&L6%-T zp^ZgX-N(}JGFWugfh_&!28*t`kfl$Y*v6u(9%SjoH@C6lZ?N?9u)(6ME@baN<)k(i zU3DQ#uNo|R!(j1WvAo@0bk&IL{qHnbbk&S3{c?jvztUjw|DW!5d(l-VviC1DSaj8j zEd3#aMOUrJ(r=>{lw#3UD@w8Y|FOZMt9E4ROMBW_bk&Y5ea>LfRXeiuCkz%{wIoYF zqPLAjS1rlX-)^wzswr9e6$Xp08k42pX0Yh0HCg)7m2E7#>P?oe6L2XOUG=6EOMXuq zEcyY|n=Jh{gGE<8%F>@OSo9YR7XNRq%C=v6Y8$KlGK1BA?P*!MYFoU`mBgEe4c7gt zPdT!upR}fpMOR(Q(kBcSU3Dr;Uu5b9qN_G#>8Beky6RJwev84Pt4@{Dg;kGAvF3YY z2Ocu}chR}J+;8ej!fAbpuK;zJw_LqQ|I1ADM~uAH-;WIT{Cxk(sm-*Wr3#MdZ&{m; zFTtl9JjQA-2X*ziS~;; zzwdxwGW6ZxGsuUkp8gd0ZseKv|68!-{{ncP`Wt_!UM&s|pyRt=mDKq)p2NYm{W9=- zuz%HGMc4m_Ih<#>DgRC2yWpq#gZOO+|CaIc$F+ZlUIhC&c^*uCs{sB0?KK~@Z-75! z_+1UY^=(P*^$_I!G4Mysd_My|mi>Dwk6mCp-XDU;4Zq)X&|d&Qb8u4c(R}vjV}@U1 zJZt@UI3F5(8u_LCSAYwq{~<7!AOE6Y1bhitHQqz8zesf5%%%BW1b))UzbIJD*j_|FQi$e%6nHK27M8r1f!}8OTLa#+ zEO}m}_CsJh{`0^$5MPEozbd$g^=YTawb!#>K)yqiQl;;ALI20YlV>pEcQyEV)8AcS z%m3fNFTOT;Hlg-E=%7Ch{yTI30!qq#=x@gN|5|YBzXx;wQDDoz7d*rGXpX-4GF9*m z2ekcR0?fZFp`?`PHwOQ~=(8kj`o9ulYMCXJHfX9Z*}0GcHlkW$GKmX zY2CjF$15G#miJL$%kRw{_zdv9{yDYo*FWj{wHbdNe9V&M+^Oh0z%}HN#)o%o#*n^UDi)w!#_+IimJr@-I3MKwsUyXg6@tp|H*e`gwk^eZDt4qHQCNRAN{0$?|^T1Yq zWpKgR$NRzO;QuFbzvlB{@E^L`Q@`&eA`aiZ_opdOIQ_a5*~$aATue--@aGZTGiec=3da4X|kpC@Qi<6sCdnnj086w*pAn;;9cx*((%vW zKlkA8I!mkhUJ3mRtPgr#rF`o~@q<4$?Cj$cU^~8@VAiy*)IPoe{s8kMOgVk*2EWnB z_jll58UJ|+e52v7g@Avn*L464rOzY4w!dZIRfgZ=*vnVJ-CiDRp2G^S`rEH@bpP$(YshD1|H7XK zAC0_H`ZvLLJiEacp`Wz0$Af=>Jq`H&&Ia!^@pb}y5%Wb|&K@rW zFE;wV9Blc27<`#&e>?bAaJv3|1$?r(|9jw%8vF#<^7|8b7x_AWoIGyDUiUZc`HV)e zi+mI}b@XGvpJRQ|=Qbq2H-SUb{!FmtzZraxk=G=6hiTsgzuVCHLa<U{|Y|JwEsHT_V>dM{8R`23pgTv&3JwbNo;;{RC0Dz^LY)pfjpD=xHxz{_(|%W zSNQfPfQQ&m9Sc@`+DrU?(2R#?XTc8R&-#azFC3GcUvc|m@SSGHXRCwcreFfw;%-ZY;df;X7){RVt4^&Xu|68(9w?e75c z-Jg)}Uh4TB4gL@OPw`Uid%)MQ-$>)rI`Ab%UK_!d-vu4GAWZ(8<|mhQ(07vvjVy1| zuYvw?WA8VDA7DL9=ldz}6^4E{_$TSuq(_et{iG@m*G{8(2zJ{<#o9{m&Z9lzti*TXNh=hfgCdmi=l0q|AE z9?k80Z1wXq z@T24lbH4pk9rWkH2QuF6p1$8<+57Ru;FD(jtH2{Bzb4iP4}#P2@wuO1iCK?{rNM#d zGnMb(f(z*XY~LTB+XSyj{gquZsR|@I_Tf&z`Mc6l3yh8c46==_pX3k9A5I0|hJ4d_F$kVD{5}KWCNuu?p&!Tm5v#Kga`)gc&J(2k zv4!AXbN|g?J6}Gx5nN{Y-4ABL?b2_>mv4igTbO*NS@!%0c%^Co1laQX6Zi+zt98Co z?SsRyKjKRRe!_==pCljsv&YAPN11;jzs11{aF>zi5Lo#(&E5WKGkAjb(y!!oKG^o( z02fY7&JU}<_kzELKlajI_}Y&4H-mQ>`Fs|*BXEO8u)qmrSbCnVB7zb z;75&oo&!H?;`ghL;Ca58@9V(!{^j6DP5q`1toro~0@C_0+R>h~0=i^A&=&+H@J-|= z2Y@wSzP>d0vKh}u!O!A9D?I&9@a<;*%vplqtwtUXfp@b%8ujge4!(@|u-fCN!3)XH zzv%I^;CC-;*PjkNlKh7Gr~MrRKGuw91^8;#Kf!{B?&{ci``{&s-BYvRv)z>ne2Yd!z#!1!=$ z|MAb@2l1}~Pu~f){Jsvp3Vm$!^hZ1BPl5OJwC!bY2Yt~|S$;=?cd=fj`aG$F{x&cd z79)Kw3C;#TLVOzY{htTE%E%pAi|56)%4EGB440=0ydQ^Z z87$2QwR+T@3+m0uk)Tv5H%6Mppgt49HlHt)r$fH+;uO6>oAIH(o^XQixu{Q7Y9+yN z>qOtWV6swa)Emu7nuOt|t;7A{_X9_Bs4usBe5I9NTo3y?b!`#Bi^Dqgig&<2>A=e3)z2 zYH_&{Obm~#4*UCUvsxn;N-M!sR44`m@nmy)q*6{L6%@*dpw_H5!hBq9%s9z6t9fJ+ zZ0Q@DSZ!J5Dy33A%%RCzr5Mg6vJS>ahflGsrlN9)0l}bJgL@E0)vDg8HG;UhB5cgT zJPOti^!IH-X%j2#z0L%gO+*ixOi#yTrV(GG9>&F}%G`r>7(m$1K+19PRLjR0#wz>% z5Ei~ZY8D%<`HQ17kwO`T2er6ft(5CA)+V79qGI7)F;lJOXQNsi#!;;}FTM46v&(Sb zGQMsus5hAPbXcub^39y2TE}!p3T1AHay!Ct_yt~dGKNAetX5{@TF}3FYyk5%_JXBP z6u2d|A3NI~+i;?9WHcOizs>Doz2GE08kh6qTU%TPOy2oC`cTTg7Vq4_1fIU9BMJ27@v*uQxsl>Rf^9RG|D#N}wR7{e&eC8MNKSds(^lH;<%kiB>H5b<=3yr!pZ^iE&C^;y_rCfEMuVpO|0c0&!d5=cd36Pds<%T_#;G$>74zIo zp<0x)Dvqun-C~E;T4NOdQ|~WE<>{s*>)b!JtZG~jiQ{n+3+!InD1$c8*OoUl+2i{Z=?I9$~TRXG_Zk1@(S;u-4Lum3cbiuyP zX!BZGoqcQOVG)xn73!5B83~JBG7{`R4TtUYB*``c){k(TBIGD51)3flUBNn(gyRBv73CVu8bQ%1V3sM?C&Ol~ z=rG3IsMG=mA6Hk(A9Dl{);<%TGJ`}%X7Y^Iaz`fQ z7QlR}Ggj}Z!gRCd48YHGj3s}8LT9op-Op!Fk_1c@NLJ}?#@-l|y7%IB9)<7pkEfPH)i-cVa#H$sH z&FYacXdGrfQKKDU=$1*}ol7Iq6-G=I*DE!JHX=01eSNmj$jzivcx7u+PD-NE)0wD< z366nKQe9v?D?&Ctl2pTaYdf3G#A&%qW-cz?Ibn&bTY0oa$fRh=0zrghd9ZrSSfC;} zIi*;++&dV{Zdv@Yh~>51K^?8JwkZ8#xXCvn61AJ<9Z71nZft`~@G_R8xrAw6X)M|$ z=0kB*P4e=AI0-;$e%sPnDXIp8*+Kd!StlYzNjzkSrJY5M9GVmmD_cL3U6lFCv40~hYyA7L!Xy-H?$gD}By!2IoE36TK-ygzH_60Qa)gY9m_X8saIBnZeh{){ z#Y1uxzC=@6&+uyPEHi^=Lokw^&TNENpk~L-22xM%W!FMvcU!k*KV^IrTuL`qU^06zxSXv3*5K!l)~x zc%>01XI!br+F8~a2wa8QMM-W~3lbyGM2aMna=G3V(budj$tK4|Zd=K5Z?JWwFrnoJ zYv|2H#PO~Dt=UW!oAsGw6C6%9r>3;+9~-xVXqJ6aZahGek!g?uvU%348apWMp4>3? zhSQlWD${jp(=Jpe!AU%tb!*FR_GW}^g2QAKftF>LgUjsPa-?Za)glt_GFf_*PuAI5 zG@I0O;FaC8Bm+cKQLz~MELV)&hMVmJ`AV5gE%4bGMFyq!Rs1d=R_aLtlh&77W0cWK zCVs2h+WV9B#DOf|7*??;t90UVYe06Rr3pBijh-118jIb$Wx{e+G~7oN6iMc(Ory2i zNh=ho?a78*-%5xyoo(9&G6g^8omqa<-Ij5Lk%8gO8S^2|oYE0zlx9&I+PE>C7#bTK zAKJWeARHOq)K*x=K{k$RrOianR)5aGYpuFp`+a382|sSk7W8IhPLz9M>(hsjYUc5|UEBB*7@;Z3N5cxnmFADmW=z zYLQdVE>OQxHGxp-EXRqd6>rvWR5{+*+jODf>!QhQoH$y!mDGrYlKn^~)Mg^fx@LWS zrV^dh>TEwUK{*h&iL>FFgrM3A)u@pjQ$~&JM>3hF_xx7usJDwC$upWHj)eVv8?7W- zxtjBl@ol3c*_x8pK(7*>E~$7zJ$*!JMmxvAYpB7JLq@e^Po1F-^ivlf9UmXIwF-OIVI#O#3o{S(V?#)(e zZ1k~Tx2l>aRP8rg547Zi+4;41c^O+#&)yYe#>qM#v$`|u2J2V4##15=y6f|$Ml{La zMlJa}lfEj{Ybd};sN3a*-8}!$bDr+xx5^>iY-vdfXSCf)0(LhW+Y;$S&>hXeK`->X zr#Ja~O3*!tGRa938sCWD9W{8qGD*n^eu=1x1zg*|E|jM#>Fa26l5fm!A&T?U?VA{P zO34yPzk+V5O^?$&SC{edtn~kT@%WWf3#}ubJT+dR?-OcWqUk!GAHCMOTYcZqL;Szs zC)NP`rRVRumh+SH)8}}0p1vTsk6t*C^L%~ zTwkkMkhWXPTdj7S@0b~+od)Z#bRK#RuGcAc{L)6|{qwW!mhP)vo_1-!yyUSwBh}#v zf2I8t!KUBU`{Gy6w7Ybl?JlJqJyrSRF7aEL0{*7`UTadgj1=n91*d_6 zg3Ao6gDj&0GvkOeHiaq$+EfZSic7-cAce6rppjNEr7gK#^LwA07SPA}{=R=aPkDXL zIk{&)=W{;WS?CQi+EHH-#fqN+@{_;8H1zXec-pUt!UlL4@Jqm>fF*!Jz;eJdfO5cU zz>9!a0b2mC1Kt4q39ujF0(=0d12h6Y27CcH2WSHP8{h+61GEFW06~D%K@{PDD8OBS z;Q#|*EZ}ayJ%Gu8EWmWYOu!t#e86LX#{o|QRsfy@JP)V@YykWounn*i@D|`NfWHAc z0X={NfLeeXZ~|}&;062>a0zf3a24h_LJjM(b|R`@M9A%=U?c?Py*c zV>xA~Ux>TUN8IEZX_k;~Y0-50k7z@nuq`NDQ^oovEVG%}F>Tp5lUS?Ms;7@LE-mMBnHj4jqJMcrh4tXrbvbX=QJs&N0f*6ov+Z$h5DCyfK49beXH#X$D}5F6)Gjc zW4UnwrQHR;DdUs)Ip#T8uFfkJtXO)OSDB+OCoWGJN!7R+tysFD4W-}oG1|Z#HHdiO zV0ls+#haw2mNVKYK{i=klb{UCy|U<7TTpM%aIIsTnYw464WZZP^SW|`YrM)8PZb%L!3xX~~Q_@RHaIBw>J z;?JuwQ~LO)PYB=HKWumEUzIL+n(1&BFdby^&~rq;pje$F)h#Sv=!&$-Msb~zXURB) zJA-1!J-`lKQC*lV{2}S6b{|Oz!+foMCfYqrH&mCxQ+%ZT6x2i;dx^W^Hhzak2A6m5CkJOuuxnAgop_!f*ZohQwz|G#-s*OXw!BfToyj#XtY(+#(t zXfLixbZs&--h}59Tqd)+j@9=1=-hVs-s;@6c$Rrqmgz>VzST$D7h_xSJTr+&8FAiN z>*_pKp%Pay#RfJx)@!;kG<}oUqEq@!W+~>mp@O=LDkR?R&)>ive?#<+=ji%jkn_TG zQGS+9bk}sRyD?kIB=2};f|qrzI}qjH7^!kIqsiz>L5n=5VZ-w(1)Y7h-y}QP?i_|1 zbWHM#!uR(1#aiz<*Dzb;skPrl5-snqivrEmxLN&SU-ZnAJ|_8uSCuowrJmW~Q_rmT zy*T2x=HF(W@lD{pzA%@{_8*_h*6hP5xWjEKThqXNDqbVnxnC0nomWXSQ8*EP7jPaB zi{D9riGXYXcLy#GHE%I39Wc)2pRJgeMU*4W<2g=NQNH?(J|v3e}T z9~9dV={;6jSsIhgmc?Y70msT_cUQKv<(ya=6a7!D-Jtrs*s{rscVC|2_?m(xeg7ccnL)^o3mKcF~lIWy-vAOu8ba#q0Dd2YAsq-tQy&7+z*XJ-kb-Ri2{3iqKT7 zUBr3iDJrjjl(IP#BTEVRT$puv3ge9&HO?dvIah3~0o;t1{4ei3^J$+Q>sRG*zXg0!tePxI4>7 z@iC=Dq`@9TlpqgR*AS&Lmr`}EHAbZANsGGE&sYCEO+Sy(NWf@ps)+S0@30o1(lDu! z;6Db?p|?5}#Y&GNXS0w*G%hyv6|`W0&wz{F9xF=MDE_Ielm`PZInW8lDU8xOg(S7v z^>f%L0X9m2jWq98;NL#LLBMf<18o`tT9^;&EcLwASzSEOJTGgI`rWP<(vqKH)HRjD zXtl&9;Rh)XWGWHI&!Q~OMq73}<_f-1%B%em4>NEROfr#0c_)`*QEgQ!1DHrLxBtt*Z(q-5cMgdDWb7 zv9a`x6wV*XC3uZFPw{M=;2kWszx&P4j32a86p~ zufs}8$TdxQw8>Z9Sy`d->in0k`SxfzFM~Rc^U~7&O@wgKx)pQdv-5x&}-)UIMiNWho1Y zw2&)_fW%>h)OtHLEho=USd=x2jEdqbhwm{nO$y#v_YT&`_C;(P1Fn;0Y7faQrHr^X zyauL(<(0~zR%1=}qJ3=&#%i`^#msowJlqpK)8Myf={hR1{%Vf)sAt5Pz;~BZ`M{=o z*&N|jj8l0xV!io^F*&pbIM93=*XDt|3T_{gh1)WkG;QR9hV%zVs%yAXi9?n{WuvoH zHqtV>O7j{aS*>tK6{+he-v1Og=VH7VgIP)ZBMAqZ&)6)29@Linn>1ULjGlgH@m-rC z$uSECLNyn6`|{sYS&XK+Jw_o}jPr(MF^qM|ZON^nolzG}7v;HI{8FU(ZfEK877r*d zBT8?^8WqKLmagTmo+i3=RT6&3({az_h`i-D{5o*?5T}<`8S6Ok8_b!w?h4S4rvVM^ zteQ(!+J<;8Eag&UUPd2ZBukdKA-5NC&1|yZWm7u5=NHrVi{o*tJ&=RIRiwZJr?PPb zUHyYRn)ZqE)pp;eRPgt5+M8=4t|)4ppc`thf)55B?X}k`Y+EpT$u3uCW5pnaSKJ|+ z{j|Bp;)KSUC_fV!l|#o(;UR`KLWeQZ zc1-XawHcA8RLw8TBhNA7*RWe(e>$qH7|+=*`zYOCi!nV2xB?iYkrq%ZN+Z`V`FR?t z#h55OA!+fAvA3m7SLc?Bc3}7)z@vZgC^1$>LXy81H47iyO%+$*{^8ch4bD-Rjp#!Z zP{LOVY1&F59bia3shx>@ZB6YDI({45YT z@lt#poTocEai|;?sWXh6Yw`ObJ(p4t`frdA-ihI~trWl4R1pKSh1pqYX3kJOo!QA` z-1M>89KXd(yjT^WqFp{N1-$;NtQI2un>?JC@aY~I&)lC5ZFi#AUpGfkFUtnZ5mfM1 z@D=bC@Fnmi%hV4sa3<)ZX{j#G`?rU+>2pSW+R>(B6jx{X|H`z4D#My&^=Ah|D@ZLPXD%{J5 z_mK7l0%zRf+K(ChMJ-Z04?&*cyjurmr|vu9_soOAz=`1d)zFzIT=S?5^DN3%!p#uK ztObsTP4}A~HtlfCw@VWs|C$|)HVmt_#?4yuI>^JpfZ&dmu%%YK>{V*s8>yf7AePeGi`Ae z;8l7sur7!+MrLwTHSJ|gZQXa7rqkbMinO>0@?Hc+Ba)VbdT0v95NqU+e~l{`crVE4 z)A(ex@*h7cB^9LKJP- zhQjy)#R0#ZnJZ|G8%X4s-vguMcVLvH)k)CbepWx0*8($+^wYc)zi3X1UxI1RH-d;0 z6i4sGtr=vNpqlz~SepK$wuZ2@;+xC?nU*bN3=}8RT38Nxxt3}zMK!}fw@B-U(swCc ztJ9Uv6%^s=XwlaGPM=T{fpw1fQ8i{sn?gdoLfx2d>5W)Hl8AIF7C0CTRP;0VT=x}= zqcDso`e0yge>xxP*IU659X-(Ld@Qm_j~47VZLw&1IwNEGkaZv!_+8M*gUZ%XD(my9 zYvj3M`fwc`mxqI4zziD74d(faiuc+fp#z^3HAAdYJ)Lz^P^ypOl~@y#g9?0;;oH4I zsa39%q9thq_>~6Xzi6 zu^Se1`X!5lfp7bQfxib8sFSvefsIw6r#PWR?(TZpwm}?L*y5iwcR1PjB@33ppS_@9 z!INgclU=YR9=}O?d;!*OCTmKk=`fPuu%5%R@ykk9X&9bX`pXGVGdrCeIR zQy*U-`q85ACo4m~bMe4$_+tK*3urn&36+a>gnpwv;&-%1^wA#CM|(tH4PVTkzCiqK z!tYRj7^@m%F|p2szNu^SZU$FMz!j5?!N7ldr`>#PC8LwHset`Wy;DIA=qP^KOALm5 zbzPp{X?aY)V=<&X`+|IG<#bx+EiIZZ)PNoahuVhaGzzh!ND?#Z-qB8dqmVSJQcyqM zC}^fN3W)&iBN2I#R$8O!#s}a9@ALx4thCNxU|(-0aB?be;*H)Z`9_|uuwdZv!^E9C zn`kRw^CxOzg3ZsQ0?2Ig9Qlk&;eG=A&3Gd9VekzN+XZ@1E6STR&$=gIE3A=NKR|C^ z7O_a94F;whCME{>83ul2Ve>jn4Cr02JV#-rlrO)RQfHZUxxd7m*Rd);7?^idVqi>M zWJOS+WlSmn^5%TVg#n{;dX=_#haR@Dc>4|q#ibqgc*ksc@453&I;#~}*TLXal8HRf zuIZXU{mnr|odV-^F(|PmI2LZ0XEEIj7FYGO`)LjpY+{NU71CV`hJ(8*>cGt{ae0#+ z36A&@DX-$YUBezqFJWDZ+AA(HiqW}Nw3U*veO@NzyyNxf*ZCRcT0f&ud(D`)HN7-n z4*%8jn!;e9ymv^gr1sznjBMp7{<1FzDOMnbnNM&)&UVwf)jNnb(ssU-B=>m}N|;m@ zHVT!eXnJ@~v?Ch&YxMKV!Y!tkEwD_9^ht_7BsjqHpN|IZcp0>#%}2|iWd#ElK*`So zzH|#UhMDh`jw>N|Px0y=$`E@)QttII`nc!kIa*e5n0I9_{m$vt9^3d;>2Yx32CnzqcYi?u31&1DAF4#GYio7cr_$C-*`$WHB z@f^So8n<|d{ep-&;x9A(#U;`27wn7jDQ>Mdn}KIL?69PR%GH6cQL2V@cZlv$kz`^D zqpiKc^+Xd>3#)FZh7_#iS}QrxX5VTFNrskq8_R#?*dXgYcSax^=Q}=Zm!p)HpoIUh zU4qtq6Lj*7e=4DvD(63Lmm;<)C__kw*e`=pUg?LmEZ_}FI=c%#Y&WNQR>V5j2(0s; zMNun$v0~c_HC?-v7#rFq9ptkNriY}_RZr!ns>9<5W6G>av!=~V&!u0@H!462_I2Mq zKpEPjFt**vJdGn*lYi(2Kccx2-qbxtOcjgp%iW`Hh1Ye{6nZQDUeT?yQeM&6P3hN2 z)cI_89NKOZ65v{e1S=!TUhO?`2t^|0^`1~|c^wT|!N6nPv0}M)#LVxGrgCc*Ej<{R zO;8h3Xy`V?yvR%}9VQ7vh6ni#!YM>-i$?daa*TA5*; zFv)1J8SI&LdMhh(URkf!#faZW+HUxU<0~zxr-vlBd*kqna^epB`rbPXzvTFJ4Zpte zM&s8I{Q9;B8m5?EZ9DA?9~i5q9&N2u{2Ffi$j6BxXt`KZR4$~tK3ylYFB72~QhvJ% z{&9fnGEvM0sB7+`D<&-%xX~4x6XSTD>pd5fgE8+=X9WXqc17pJ%!t9P?d%!?nUJou ze|3e~68U(?W~TRCgw=FInxAzu67%s=m&{6KgwMP7Iyk?IQ>1o#A%B3cwfBNv!w*(q zuTEoo(e+G>1misUfd8&Mr`L$w_@kiq**MsXchRvd3y!2^G1>bK>tV4t&|T@L77Xm}rp_^m5@K#d zp21)T?U6mOevs`^9op##K@Qm;0J1= z^P+P@`RO_wiIkDnP^l5NCP!2CtAgB^2Dveqz8j%n;3!HpI2au*FOxUexvIMF{8%-@ zAzG#hW#$41!eea7Imy=XSN&OE3&)*}!qbo-M&&gMCDSI2&g&?97`U=>G9-y2ND`|c zML35x3TyFu{UeRSE7C?`Bff0~Y{&Oqqck%#R?7QU3HS5BGf_KyRxtBJXAA{St-x1c zn)vleAtpp|(4WTz9j~?qFc)^g$oB`v_9ugi`Lp^1N%d5gc*aW<-xL9d(O$KL$Y}em zfLRFBak3Beh4!ugxAGB`uWSiQZBl4XmBJJ)l`7~8TL}4;&ex)E7_HVNNoK8~-3i88 zG(9$&X#G(XFQBW~)^32-qh->e^=OZ6*jK%|daD_0z@u|DZeR;{R-aiEUevfD3n3=s zKu_BZrYAaMXLaLhMti3|6kgjO$O~dkXhZh+B9xtI;|p(>y=KE^?4VTl-0YC`#OjaZ z*FMw~?%gP8P1&yFsF|5~u;*r{tY>KUzUt$v>6fkM;qJ8?q=oyci8j$a_K`KKX?bj0 zF#U8j-LIx?j<3CC)fv!lR=zKznw*vxIH$!~xw>(67+Oc%bS;WF%v1jGcXwzEvPZ%w3|rjpYSL2XGe@O|)xFS8~Ne7dRBaztZWu$;=~3x2&|`DVync^%xeybd-y z+Lo|9)wI`~=!rs^=YwjI;`9eO z#XKBBLwk1%5~Hcbl5TF1T9DIVvty^#N_72!@SxJ_gg+m6cEPb3HB#Att}~+vZ978w z?{>6X1wR<ng zla(n=)v%77m%QWAglA%mgVd_*uZgm~SVbjT<{enr~ztm0sxEhtC$%YMeNR(QZ@e=^x(Uoxn3pF!TyJ($~?*xcBeWVFH2wFS`lgxYNb_A%yO*s(JH2u)lixd2_A+OH^H zQynB2c&%Tw`yl_jpaFi=OV{FPYLilL6&}>u5~*FvTuXJTCzd_B)UYfyduPJ&tLl50 z^tFgbtU9iC&=warQ)A$I$RqpF%Ki2y9owgG74A1?xZZK37Va>!UpXM}mjqY7kJ7bs z#FA0RfPL&iqeZO6zHI)|z01P06XK6wjfz3}ShIuW7Y(dgO8clJ|5vQ)VBpVg7Vcg* zm15s?Q)zLxTMBoFo63fNaLeItbt~X*ax3Bf&K(B#Wp_B-^=_)wtaH zca>WWx6~a4_gQx|++ueO+#+`@+~2r|!d>bf2KU$QyWkeMsTP;-rV`z+-0^T1x~W_@ z&z%VO5%+Mov)v=$=DA10&2?+x&T#AC@@_p`i`xL#P>KesLz7?eZ3j()p`ru zZ|k$+Uap@8x4E8&d#Qdp+za(qxc{u50rzbE18}|d55oPtJ_qipdK=t-)aSxIQ9l#z zvHDqX-SrQ{Jz75-Zf*S>xbN4`g?pg>5x7>~PM$+tkXIbj9 zGn$cZ`Spz{hpzv+4E`hmzRLaeHPzCOu8&zt4593}2ah@T6vjNnr5I{)=|oS#*wqNshB z(J4SZzc@<6LtyKrJ~+OvLkw}&Fx?M6uaC;bR1T&xaZ!!xAt}!!Wt6a~L%iI$SXjUr z!yFd_$&Q(L1zh0Mpmf|1pdQRPzi%v0&va0|^uN`;>3^(y%m355=lqX#Kk|R2ZZ=s7 zZWRu072y>}oR?w5hyAE#Rvhu+x9ZERk>ZpElf)*`F&)d(QEduJ#s$2Ybqx5QgdY!^ z{d5mWUgo8?=t(H?RL}iZJx{sc$)0-=;t=|o932grPm1qv^&}xALuglz4k23RA9_Y2M9bWaGF7?+l=L3T z9HKkxi$e@8Z(Yw_2t^{~?1@20g;04<6hgFp&-O&1Hd@w}UKK*Ltl#x2t+a*xfepQK zgk-uyKFaeJ^-yeH0h^ZEM0wEMBNPWIE;@kEl&6kv$1$K^VTp_i7Ld zM`&m-1Ai!5VCoG90^O85aY3T(4@~I6sWtfN@Y&FBF|I!_r@yMR`!U#wv@~^D50%a7 znElXwdq<`}@ZI4%*DFy`8!6FAc*^i5wE`H{#4Lhb1BySlcr=v%iM@fYJ#%zk_poyWPzL?Y{WGYWK8T z?Ka(N_oKJkJ@1Zo9~@}+xLfTWbF1Cix7t1Rj&{e0?M}Yc?(qMy-Ny#nUH`vo_tmb! z@xIbEINr>ycK3GQ9`Dx%+U@BY9Pd-GHH60dyY9j9zKk4$Obu3z37D zP#YDsD?7VT4uLEs)+fW4=TP6E-uTcbjCKk1$5#f{aE$G~8sHr)h*WlrvMyT6@a7xp zXVy42%cIYcq*DXhIn~RF;gmhrMP=iM(4#?JVUK&k(&&VD%-?*)Bw=TFK$ras%BQx1 zl*8(ps9dVcCoE;P+owlj)lC%)j)>fZrF4yLpWYvc?9&z%xx!JdzKd#Um!X@0pLY?l z?+R$tJC91Ooj$5hIbYjOD6SqFX#dyn=}ytjS}H3<@KXLw?63K=XlXr%l3WHk>!sF- zP9gbG$S6S*!aoSmO3nVJ79X4UwV-g4q=1jovzJg-(ZQAP$IaM;-Qp(`S3{;|*IU+a zuHIR_!#u)MSs3q#mPMR1ug`Y8;MiQfXVC(?8L_uD;LX)$XZCt_eMsZJ;D~k8Js~~d zf*qyJ8d_U)v@h@{chS*7y_L%G!)-4(ZtK+Sk4ntOuRC?>Y?OVcPMwYVLOQi%{WJ%3 z>LjXDv(6vs)JZ?qse^%6U;z&8NypHgy*4?c#VG$fsHkPD)ODC^HN{w0kj{Eps>F*^ zbUE?%-oUb8NvhA=4Y{H>$Y_74|J%Prn?UZgLeXv@?v7U${>o9gA!J$Tydi~U;TV2x z#t4db0j=3}Y`|*J*(U9Y(r>Olwwiv~VU~N=Y+wpESO0D<^!q~2MfH-9u0*w)g#Sao zs4TqTI4$ozd%;1q_zSSh(S6F5??=ysjs@!nG%W3!!eNeT8FLPDXfQ)A^Xx5Lcn3z) zUdn!?kr^-obmzV=aJruET8g#>;L>TTcfr20fhcp7wrbc+U^i(m&{4}t0+UXyDD70g zw6w4>mDEC*@BtyOynsE}sWYeMq!v!iH9cfNd}{twTXac+gH734h;_$I^Kw`NsfDTe z`yGakBFldJeut%l%-L_hm+l1ku_K4wAw7I*)u+EN*oooz)y#~7QF$Fs?)cS7bCl5U z&XeQ9MSEYMwKqAmgRoM}2kV!Z4}K`$D;alb3-qC!zXLT%W{(Tw7KeKL*-=Jp3$?n5 z?Lm9R_Tc2^etWdo9_$>7?ZKT2u|0I$X?sRz$g~BzP`Sy_2REWUTkY@rNG5DvSg%xW zzElr;0=4gA&q0RUGhV6e?1I8*Id^tmAw69-aE{qcGCZ|xtR(*UWxFED+e zq$YX7Sw((=N)K?<8g?tsbNyk~D?Wv_3+p!MWJr*!Xe0&1L>$BfJAgs~7y{@A z{Y$35Yocx2&0*dcZC~Jvo>;h__6!rPkbQxVj;IhP_fEyv4|_~->wC;vzfUfH&B5>Y zdu(tI^vr~`fnCgjddM=^yYDFX4`R7nZ#P<(vJ(-fGWVj^s_=@x~0Qup*VgV;ZUA0`>Z!3F?X?~FL1K& zGA!O|PRn1m!=es4H#jSodzne6#SvEF^fRR9TO)k3PmWn>>?7LDI+^t*#(A*cgX3!% z7~e|R$emoJ-2z|2S%9Smn*~;0>u5dpzjH?u?0nA}-6+f*Jrcj?!G=xDjl!bQiMBI> zDJ{N`r1V~h7gt1letcmxFu%t;Y{pmiuN+w&Z$A8$9V;;H^(AqREWeY9vs2#$zfm9) zuw$pm!l@O6HOu|ZtoixM8L#Ivy4UR%ZKEJb?hAYdd5W&n&M%3=et8%XaJCQvzv;_E zs#yvf`a46ek6)t9feqFkZjZ5B@@@HZ@*C}^>}Tj|bJM+(L0y%~!I?Ev^+T{~B$$;m zy@eyVu|uE%;~aRLZma|61T+}!$UewPZCZrF`sh9x<}P2-HV(fJ_bw@m=7mBA}eDur1=X~@$!7!`3rcpSq#h6}A*DI2=pnG?( zXuLWxx>9H`(Otg32lG0L7E_C^X+>XPHFP40t}k$+H(c~T>E(h&4(iTa?5Utn1>qCf}!(Z;0? z3)usdL!xNUCmJ6WB(}c5gGcGFOk==D{VB$F(Ki`?@X`39>4BP=Gm{qn8GPu%oNhT$pC>jQ+N|{RkaL+Lz*~ePt^`Utb%w)KV^7_mwq~ zzg+)w+2!K3_!=rti<}>cIn@_CuQ!OHA?LonmQWVfOiTI-^J*&IyZQ~^2>zUpe(6Oy zvDVM7F=eo9Yxk_6b2vMg>e%_@L$!O>=hQZr6Wo7fw4wbX&2)OBj^26WU<1Cr2T#aE z^YZA*Nre2vOz0oxlEuyz-9bpDXM_|4deu$0+{UHRRkuOGbvoye*44~<@qB2!!z4&j z1@Y`{l(;VaHR@chQ~fy8HdQ5UHQyy^$({HMMrE0j|MdK9za;zVhpjFy4W-Q8ppdk( z8MLg`D9c|r3nj(dLuC;nj5LiEY9jf!-KH`1)he8~N`pj8?K%g|Lu?UW4auSHc_4dV z!XuCG5$2iB2osYjcWi_&)FDoHS|F1_VU`?&-w8TI*D#e~G-5lsPO=#LS7?dRzA4|E zAC>=beo)saDB@PzDSgm&^ygFhVA9P;I#bl^i%n^u4N-X=YxCps;|j6!e^#xdp^p)o z14+eMJjc%3IGpDR)ujRHP$qA68;!p-(o+OVTbS79YGIKYwJ+J}Xlx&1_&A<&-8I4j z^&VjnZn)lVryBBi6y51j=Oj|8b&K#TZCPyu?!eH#*==ufigV$)$AlsqeubO-64NpG zND2FPX#Lz2eOe~%H|;;>laUSff5`%e#zjwJEdb5Z!|xX7kr@0nGJZI(p}UY1aSpf% z_tXTgK_-xf$GX?{e>;t|G4gOPbB}bq)LY;EyN&{7$0IK$GNYJ`O#WM60T1qKD5^>1 zvv0iq$jfF`lSLDuPwHmH8(PdHoGA_uoMzhSP1=)-7L)f|w@%f#mzs{XM@X2AZ*jU; zkxi0znI%mm^SxH-Oxvgz$|1Ywu`ZgMv!rm3%6tS-I7jvXG%;n#qXo~Pp0kt zs-?gf70W_eGXWqx&KnM?$I5p0UDEG6DA|ByZ#PlAbZplL4RO^eW>tlxqAE z1;c9{_j>3J1&@BSD%>D>I~b@BvId6d^fY`ZsCO`YwBcC0SNvTSq9cWoHD3ApImUTp7Gqlw%g)=CMZ~s%yPio|*sBx2IGg zmQ)dBCNlh2z~{%nN9h@EjV&%G&RPWeu1cD5(*z!=Ng_$ZW;P1y?9|aFaM46)J&FRH zDa)pG^fA!&O7w6;>%y&lffu@rd@uGnmEu`42I-#f8kbM^Fet4b?}SXG zfvf}=2T%bNE=W1Gp)<*SfkUik4~EdDF%|amja9$c-XnlP>9xJdhKX9W5~9m=uFI zeDcd?t>=EoCC_ys1=Ei+FZbbBQCDB!mt9Xj#bl7tVW|_II4jbT%KX9(=342kU z5%!k)f%b7Wp zUCgAl2*-f`uSK5o0x_a=CSe)1l4z#W-wT&*pT7MFzNRs$ccbKDBLh813O(pq<0n4a z7B&TXkyzI$-vpcs+A6HTvlJxN<)LSU>}gwtmBz1yDNd65cWzYf0BlqF_%?W_9MwW(MoNNufd(vd2H|uSe$h#)3h2HJpaPui{M(7?r zTM~_T8h*EESWa|_gb%A_Qgt2!`0s(v*ovWiocGUd3hr0tUuA8sm4I4M%Z4{db+s_B zWZ*pW$3Z22vtBX*Yim?8EI?$m&o`VO#(yK;iKBh|y!&te8WCOHiDXbzW zLGKgqb>zYZX@sR_T@}5P^<{7f_lZS)>m=vG?jT)zhC>FuUf+Lg8QTw8?S zYEx=u7+HDBE?{~*TKrqISX0!EI~a6aXq(kIIWog_MsR9zPv5;oSgTzlj>TH7c?6RZ zlZ(?u>JeW_k`p~&)#^*=&0@&T`qS0=B3jQH+;o!Q#v9fWcBB*tF}ROR>yixIeLKGx zR);&rh$|ThF72=J;+CVtF?=RzQ^@I^a565Qb(y1ak2uCK>|%4Z z#iSW~j}T+5#7%%5)s?tS+}JKR;0tlKuv%xe%IoZ4aF%d4@z>pjo7crAntG0DXZ2S; z?O2K1*)bBa3&{^6PAt2|Xr&mZbw)$21Ts`XT9ZSAv)lAtlEJkrv1v*)7{eEch%3OC z!82+^f2LZI6eFHzv*0|N#l%c$!g;or=fY;%>8$txC+;$9B4#qM#1K#tqZ+q`XRH&_ zjCX6-3F*dl!i#|QI1%<0taL_W>K(VqGI6(15b=rLF-^vNW{Nm~d*SR(T5AW%le7gV z;^wtj^FCN{$+&i}PKY*!YHva9Txn5_#6e1i%naSgY-l%2h;sZQqTPtwqrm&R2*)4I zQtv~KKV#+m&hd9p6~?Ga`vH0d?x2hJ$)apUHBmFqTxFwWB0kp$FKMGvBz%;IOHp7i zFqDTfR)n^T(=AF3R&W5wwMD6P*cd^Cs*HRMxKNLCuTg2mWG?^PU$ZMemu1!dDc1 zV2<*J7pxYhG>0!!^>FQ9Xim-Bi@Uw$j{Rn(_lV;F?(tJw5|hEDD%xTdO0Y=gO9viV63wxMh=v+Nyx@LZW3~d+V%1>Nvh-$_Sv(qx)iNiEeTvy~O|taRbsq5RvXj?eC<`-C zUGc8#8_UWqgM0iP*S{5Y%=6GwpC~(sc&fF23X2~xtQO`Pqb?D{dhkQY^yy0+Tg?Bs zh?A-MudgYVel2L6L!lYWQ{M1VX?c5)Bx7brwTSpflAfMnOO)X@C2ln6*&c>0T*^Hz z-lemsKwZ68Nwgffh6WkP=2}NE@LjMlajM7QNP`V{9_(*Y!IDkJPVuaSedm5p{IXar z>0#5O%SlSQCzD@4y|{2S&R$Z!^w(CxTC(ZeA(cWlF*sa>mTcXD!5p*8Z@6gsu{cl9b;kF5O9~y2%3lTv|pmXI|{D&7z5&s z*F~M;Yux1v&pj^`=)z`uBLNHahc*V z?j?f$u7hPJPvoI-pe0+?_fu-2)z|J2Em(M42NZG}(!HRbgqYXVQE-#hN*B?^ffBz4 zE$|Oo8ngW;G269ggk7M<(yz3L)fvNUgMpK`&ID3CdTqwm#8=nyla#-+9_W0xLM^b(AGMq1W4-?=eY2FM_Z0L*zgkGvlJO=#v-+hn zlJczQK|NjFQ&?>tl(MZv zuPvjly^PJCt*Gw2S%DjnT1JZzA|DEGYF{ecCV%iOi~Ai1@G{c=%b(XTE+19CS;@Ap zV|~6WskHYTM*WkD|9D?>9ge4K=U+anW25=PMMnRcIcMdU9#((d$4vOnb0xLQ%i=|z z54vB(N$69!r#iJG=e@9^={_>PqT{~zOgCObAE$QAcu)8?B!5s{l!G=VVmTyH8*Hc) z|7{}}J*9wbH`l)1yvMC;`S%KX=ak;N0qvM9V}6vGZD zZ5n2_Aep?jaIg7z;URZr{$BGx+>QA!ncKmya38glbI3}5Bc0ZAW)xm&u=8s zSq|hnlXoVSrHo5%$$(#Ut2-J@)Fu`TKrfnbFUISIjNRs0=x0butnZfb^gO>7r3{f_ zkF;Ag;hFOd?L&wPb{slOnV`POWC7kbSb?1P*rs}F@XpEw?-odbB;_IaCGc~+i!thx zn%M~w?>Mm)B=rc+B9RHbsPkdNZtVZ1p(f~(-3{$wIJxS~H$Vd`3Dy92+R%^By-v(` z14I0Bg1Z(yHF(!$r1vu3&B=mRrZwld_LSw9%_gU-XSaBxLu*xr zm=bM@YjJsK1y*7O{;6}8_)dr%ZCYhCxG-ksZNL`|XkQWReDBvW+E+z7e(_oGWF2M! zGro2yz#jnH0lx(#1J;_p#~qwg!3pU5KZ4`oo)Fz*!Er^9bNW+lkRKW#Y5luK zI8Krw4ZUJId6i_Jyvk_<5uMWachr@)Bx| zc~RCQaV7#krWktz#n>(Pi{nFS`V{D^;~)#uu~NaOG~NoiYrR==aibZhQ-LobPVTQc zeO0#j(pBkPlJc&5k4B%EmvGp8$UTWCW8ZZ@rWu)d*!(trtuYU6A`{NKkGIMs1?C~y z@8TD`_3lRwhE)M)37*d|hK6*7TQF6+72*IX)u$0BXtLIjm4x(}`AwDKx)~n!@?Y zAJs(5nWKqGtTZc|Xv$+q`LKEQK$=ib*H*oXH!m(-Bd}+Otk@|{m#%S3tFAv0CaHqn zV}K>2wUS{oHeDl2zrLnffM48@)-~*zYs}Kq*SNC#uJdKjK{_#B=fe(O|EA1-eawKQ zQg!|DvKOzv7*=$B^wKYVyJ7i0c>SX=Dz}_DyBn7DOV^}J2fv)eyAiDZW8YmLcr$CR zH;Ad~uamNy04noLM?KE4KC}^X5s2E_Dtexcd)xV41lC((w{&N+>(W&&`YlMld(5&+ z&D2UOo-z`90bBZyb8y|6wL^Al68`5@{w|eRZrK02+<$f{#2m8!b&mQjd#!=q7B?uj zzX>eV@Yp#b;&~f!{M&ts8WxhvJ6#%RkdQF&ZjonwXs8!#JKbe1MnyK5jQF-{h5B#(P&!Gx;M{ zx2y*}>%yJt+BLN=DYoKm=P!S@?a(QJC+6TAK3dim=Qbg%;mNYRmPLuVEsd(y^+!9M%NHIV z*P{@j)rPId8}7!NyUGc=ih}(Q@t{fVir>T&@ZuEsm9$@$v1b;fIWl2irXK^58e%XMviFTpugt_js6O; zw^}}&(y`wsKXe>zoeZ0lykUQ7UP~fc$|WA{B+K7M3!RIe;+X@((bv!{B~GF9L}!V% zs;ECSOB&l7<}Yv6IR@6G?Pm3@I!6W-%@Mhmd>?d@ii(b5@6kI0ANP}FosT3P@i9qX z1NI@TfG-=cS#*G2YQMp`CE`@)!f&g(pkW_$k~LKn3tz07pDW#@{_E=RRAj5LmY&PX zlJb|X#_hj!HLBSwBsqt<{#__sM3-CAL-dYd`t;xEnbQR*~?8M9lM z+i+Icr#{-oM4G&J?c}x$+xeM5nm@bBa51pHn{x0i?SY5Ujp zofQrmV|TpW%5gi)rWQGAsGj0JF1$SpJLpbYBj+7fOUBc6FWq^$mOOyasNF(Q(MPqr z4*)Bb^hC(r8ON`V1&m*B?r0xyGmF?4z1;f-Vc3b)Meiz2hIu&#)Knh zg`?Vp*Jp!*XYyx-L)y1nS$30oyIYQ(h2vKztYI=1P6L-fT0m7MPm(|O(zY;=dro@f zbiVWGYc2~>f*kKYqCA>%7LzeY%twD6ee*J8x{8h`-!t-myv6e=Kl9^Vrok7X@XAoy z%jVrxET8DL*1Zsymz8xCJt1#XNk?QyezuH{JiZOMLb3Q+^B;gQC)>lZHV*bt zoN1^Yj{U|1c%xATxsy~Fuy#-3RVY3De?wL^dl>CC-|EuFQWY!dXf8<1K8Tfr7Z;^@ z{$;FjhJ#Ob@9QK5J%PI3@iyFYKSiy1{(g@S=jHo8^NoeQMP0k4v`PNSS%dADW!SlE zh$;KBRE5?B74lZgT|a3dC+i@;*o9UeioRA@L3NxhBXxF({-O+HN<>&btKL^y!cF7j&AdOSn)r*I(*J& zS4YQ8u)Qj**CuayRoJIprXTrN2j3Go)N|?TaD$GgHyv6Buxz=HmbtelucKilXFzU) zTvr4M0OiJ{YZGm{EhrV=4J=BHj>|!*#^lW?)p%y0wGF+opl0zRHKQj05A}v{!wjqc zDngPgIv#v)Zz<=ORu=#M6jNi^Lh0f5SX6YN1u)T2Ed44;)tF#FeT9QOpM0N`& z+1KeTFnh?@iC)@EV~@;`*YVye^s;f}mLkJ5W!f!c&_k(TE}dljN0>92UISm<3ZOz z5opWcYq$sSg4SVR{9KWv^ap}H4ect+F=BP^?Id#^H9u}EDBB{8QXj{g$##AI0N%K| zlhOOKBGk-{-14X`uj9#Qr(mA7OBjOzGrHcGu20%5$CuHDU*;rl9{Ja0dIwKux+k!+ zJA^ru=6>EAqEC}>vXPGJx^CQ3&Pdnu=*RGMlv+%2XBx(J3NVM3YuI!eHULG%5YR3i zbl>6d_3$5n|KiFm=7&5)d)CKdhoL|4de8m1-VVpl3S@XzOI@BXuozLSbKl5fNp{}E^4ObG89C?~&X_69YU~-NN(i9^iTVLcC0>5m#_A-k9AE%n0q2^am!=c+f~G;X`lq z()o0zvm_h1PH`MJAB@Hy&24qXZp`iQfw_&Mb89%phUWJ1valI+Ru-9i0^Tl#jqcs6 zPK9uk9hjHl*l7}9f_5)M9R&b&4fcKf5s|y9IDVX$1&^AIopP@pZxieA_NR9(xJcBD zBzBoz(ot04;1ex9fjwOjHp)L#r=TsSQQmR7Cp7Pg^mK2E!Yj{FmyqOucYCQtd`0hu zXw7a$edhrbXU#KSqPMRstY5)x6eLdif^gV#tRt_*I?vj2I{6UZiQ8Qz=Qn^yW|eVe zl#@B1PPE|l=AoyQl#gYAk7?Wm=(_vFwCAsOC0kBZ!|q4E@U1D6l+$xkSvQYA>}oYfn#=QA=Ei2X9FyEC5poO} z6+7jOkBqzGk)qtw12WrO+z`qId(#3Xjh$d4|uJS+Zld@Bo*5lg(^F@(^9)za6wie{*RsHLCPy9S|mHE zj`Kz6Ww`BhUL_U@`g>b;+s>cyeJyNvy>V^Hq^4`KGJ0-oNZb0bkFHlv+VLGnUImQU zA9?D7>m;Z6`teCs*O!FhJpv(l{}#mfuV?2pUzg_4R7prhWBaaOfs{KzKwKH(%FYQ@ zMX}g5=tIfmZs@@|pSp+hqj+j#*ez5UAIP4PKvMHPP0;3P?Jr)hgpQqhYqwzTrKW3g zF^0AW?GkIFZF>6p+MKHE1!4VxKrqxE?R9AwZ41@7L+vRDLyhnB2fht{Exg+xef^@K zG162E<7pbPJtXl zK5ipiL{lj3U#EpP?Z)@ve2rPrbdc%-c-to3m&>>LX7U}-L&kyHow+LGaQELVZDKO3 zXqwT?=epbr2oEgoP z#E6ti%45e$lHh$GvaiA$3}guutB+S}`9!+vU!?D<|51Erz0&YUvjtebDdvyn9MAiB zg`N2S=46tRnj6=Pnxqg19rEo?XXf|((gTaZC+I2<@fNzuDQ_`>^Xv#tUNzaZtSlB9 zqRc~EZ-QJHs`dTOmgOIc6q)LX(}mff^hS}=)8A7Cl3eXcZl0Oz0k6>E%f0yWSMRb7 z^j=nha-Z*o(kk47*eEDBZWO|u3lBrb4%<~7FZl(Z*D_yeg{1DB7*VVAGqUnk8L*{e zT)kx65)Vmw!FRt%@w;KwyU(VD98X`&cxXVcL=NRA)2MF#&=K0=3EsPNwiKQ&B&mno z3(X`2vk&?9ZFda+S}J#mrl4uZ{FgMQ8y9E8`W~419!=TcwTg3i4r5Uu=JWU_@`8`{ zWL@d1RSOUQv6^b1C4i(3$?=CF8ewaZfVo z7a~shK`SEJ2T9^pyypv;0-!cQs!LJXFDsAQsgJJujdT|zK5DC8d!(jw)AHfaD`d{b zQUghW2_v*mo`N0e&jwWtzbgsQ;jaSMWgNy{H7oJfJ$pQUVRktuL)tz z)TR^4W7Q}+{3Vp$()8Arf06#cBmFe(LA0XQaAbhS9>UmE0gNp~L3!$((qN+~G0gEU zpSck>iWf^GKN~E6DvOpV8+p2BINAse>}s^-BqMNEk3 zF#VS1c&J4w=IRg35_4o)J`(#t^C)nRNV)O<%tLFT`LGK5_d*jh#f-FMiV_mlX{jz& znMx(WPtv#GK9uRk8TeZGU&5aQF7>^yu@o=ZL*5?Yo&p)~x5i3@H_N$mnWDsT(bL$* z5CUrOPhTZ0Tp=yb>!a`AuP&Qj)=>5fCEwC0i)IbwNo8z@Fgwbh2zf_>mz$YUI5F_p z2=5GKR?A0>tFx>^dP-*S30KL45{VY|F6Aofs3+A|3B}{0K-5!TvQOXcK{W|%K+MX&0#>~;$1WIk|3ai zB8-40Xn#>m(Q>!UO0$bewbgDVUfReE3p3knml54`t)$FIQA3619bn-9ectySeRXYX zYuwLgKfex~Ip;l>=RD^*&$*xToT4P}P7PMbrT>Vd6efC?VD9Z#&Y%`vu{2A8HoLmc~tMV6K2BInOFf|BI@JrCmnMDEuEc zKeKb7t*UT!;pvNAF%}6<+J;jctHV@9apyM$%rt8+>4&|2>F|c^&>@$4Zy^5P-?|^) z`jmrsNyYb*pi6q40X3n&JX!?LUr;@)-(OW2&@i$i=2DQha2cHEgYkJIN&{opO7?mC z7ynn)oPnu*t9kN$!!PR&rqTyJv2k@s|rlUP+w`Z(vr{H7E zOq}`7RVnF2nFR&a>lAadSx)H@qaRDvOJ$O520BX68FVnytH} zaMN)rZgq26VX#-=u{~I6yhnu96|E3K$!+|5`d0K>B+s=FwpUb4NhGxY&K#a6Dh{o2EdO+j;em4 z@Jyk)o(nS+(QjIj=}J$dPyEjMDUi`Lh1(;}U@6`Q!ghT`EhK4jwC8C)&q8-J)l60m z>NxBXb3lBfjZPsvk8%%6Zoc&QqTn9$FRjCw`Yz(?K6nqOp(FICF9sIzmwICiac3)+ z;0KDGbG+0vRNTMPrG%^qtDwH~S?pfXy8IARoBiImhwP^|m*$(UJsILd-DhDV=FOXD zqQ0B@Z2@JgL5a?pkl{hSbo}|F;hqWate~?c^KIsRd+?UXEZeH0s-o$M-YTwf#iZc4 z7n<0lHv21%cUIudoXQD}CHXi9o?nL3Ffq&7aNr!a4*6z%XJLO&7xs*KX7wJ7TIb>S zvE1U?35^dUY^^Q7C>r^U$4!9v1tBVRqcuH1K9NyZwC|F)?vL2rXt-oTn31~qmx^JI z(7A2{9IGtHd5)dXd*rhl(rJy}*vD8WL>O_VwS`MCCjJRG1s=)&2Dnx{_7|s;;h5<4qeePfs9E zThXdbL!N?d4Uj-j5)`UWmfXqFTL+N2unv4w(nA$__@w6$PF@Ft%#gNyE5I?0E3r>p ziMSeiH3hRsoUyV^(#O(aevCHUE{ z_Yzn&MB8C%_-k=y%FG8<7(;(pe4wb=KGT7d7br_w*Y8yrW|=)s8i&2r^ufw*HpBAt zUF;cX%zp;dx-@M42G*}+k(;U)f^#N#C zdfAA2@5{Wl7CARv$qabb`~XU!7F@{5JKeMgz7}+?0+(%vykd#(T)egxv~R~+<1Ca_ zC+wV)^mG-V_pN@ce;QgpZeu|HD278;rrEeF| z>8l6CTFZ)D_4q{PWw^L*`*1jC6@#-v4oHf4QV8Wk%eU-%si{ijAL%CQh5cdcPhaeY z)Pk?2)+L4JV-=TlMRboyncC@#{tIqA+qDjD=;@0c+Nps(u z+sH0|nKYE@B%2sFhUzHEepWA|GBlwKsivW|^rEutX!{3N6GL!*PG>7C`l@JXB2l_F z9X(lEaJErLb9a){ZY4m_nyyssAa#IiGg>U{HWU##OU{abn1A z;;FnHMW|cgNj5wTCza_vfCwYfl*{>FjAs6 z%W03;uNt_^MrXB&x2d0@zJ?qj<;oVsKERK=-$2c98GPUn*_Gbky zVHC=3e%o7EC+cYn-^Ydf)xOoF$D1^7H9fns2Y#PhIj7n*;F4`m#h>JMgZd;|RlsO9 z7WY-(eFn6_+#2l^sLrV6CixvK{6gI`<`$b8H$rf)&w-mQ-hS8g6cv^QP{pJ%rqKk+>5r2aP&fNu3y>JwyLmm;>w{;dx{gI zB|fa{bTgd-IEx;XJassJPGrJ|=L>0Y>A+ zc6&PViIdr4-z~QD;u)-Q$*xman}ps*I)SwtC$YLDSZu2PUBvYUr(G{x=kMGGZ!R|R zzBs47z14JODq8I+wy>g8-c;s2P(vR=4ZRa>_opCPa&r)k|?eU+ikAGc|)QS)|Vao z>(tBN$4g*w=U1tfIMVw`&zj%B9_^oP9RDYgw~429pOp<)j!(H|evsfV^_x!@dx8g~ zvCOUkJ#6X(t?`S(rwdgvkYgCS#q1Yvx3NC)DOj~K;Po8fj<-WFVi}cOfEJ--rCID_ zi$SqcybJWS`jZ+g0Y~~8`B7gPNm0GsE4?ALm@NHL93fbtk z^}ffkn@49S!S(#hO^>6Wqh5~qaoIZ*zpPx1J;Nx5-BAwXDV0wGEALwVgbu4N7{JRc!bhyyJDaIk!#~?*ET7?-QjzDTKkE_Oc zy0{`$(J5}q`N?Y6s35}lB(R{l&R)Es)Wa4=ZIPPB80v}`>r0o9s;^^w=R4K?>qHnt zA=BdNUa{b=o{5Pfl{U4U#QbvucqJ0N{iK*DlOj(~%6WPcd5W^G74x$eIeXgc6XfY| z-QK29j9huV?!zg@)n!7MIL@T;&|eTHb)_euOKlU4XZD-kSQiIBxXx<2}C?Oq4&?+t{! zFRj4=S(5!=>j>LpZIOw1kp{K}q7<**i4J?FQ<6Xi_|7oMlk$b{gH8jaY3X2_<|MgV zKCn%4c9Y%4LhhdAHU?7nq;MHdatQ#uIZlpmw(n?ef$ZpJdn~@NBZ(7A4*N35XF}g# z-wsm+^BE_D-~48;l;CBmJ{sIe(3%)VNRL)hK1I0#kSRcS#wb7C+s$%*2DFWm^V6s8 zZaF_HF+anQz7ulCa5@(lcXi$cn+I&2oP*??>768PjB<9ab)cM!Z(2#(7`dHxk_2ta z?LVEpWj8ydjY)1-T0l!+?^;!%J@7h(!+yr8GIUF9u|L=jx$pLBp#@Ud zn(gnmQvUA~^DneY`Hz?LZxZt#*Q^)wud-3z{o9-EorTyR?v2@E-i#KCftDIk;+O_G z=zw(Igp5HKZRlb4wh;36!I>WxwVxfw;4gBg<{?*$MeLzQ;ySG zDevSqOUygD-Rq>hlUo7uE>R)3m6xfoz}X!*so)z2D)^xXao8VqszQTBddvhpx_}-{ zkX+IVIqEI;tIf^!p&~sxiuAC`^k{%d{BQw&RDQ%2fz93us;m>Gp*BOl)2;K4`{?yd zUo0%}llY%z(#E}-WxROD=V(}{cOb^{ug$_pl?%r@!R0`@`={-DhdAbu`r1QS&pE8u z)b4JYjCXaPi}b2C?$`EhMF@Q|&3P}@Y5O*bqllT_TkecA$HjhAr|NTopy7nicYyXS ze5H}ZYT=NPg#n)N^K19)^k_qTarW1vZM&H}io1{dNp@G8bGuonAz5Ay*sj))9KJ@_ zFdOA*b(K|a_hSv-l;WmtP=ZTtH(IGM{oc66(u>cU1luf6NK3D8PhCu7 z7&@=6rL`d3ER%e`Y?t=Bwfo7Bg@IBy@$<#5*UapMasMPC3@4~w66f)ab$8%i9ajGQ zR-1HYsm&>7o6zQHldFXb+$P}&k5zF3 z5iU%^D>(tjg`nCR;cuybyISl!FTM)44%kO25st#e1hWRFs&k3(>49}<&yJ;TPUunY zh1-iI!tvBI!4uB+m|G&8;9;6egp)k0H>u{vcOSn=5O6~%$k(@pr5>~&P&^DLaii~_kQP;i-}9EEo?5(Z@U{5;HNHdfLhgB_6_HRQ;O(Yp z%qmP(u(jB`?r#7+U3sIs;w0M~Zz-YPHlwaWvY;sOwy~0npdO>=iEa}<6CO3J#)>%? z(RF;txbC))#Af>|%_Ps$xAHhBcwA`YTI{1*j|&Yjhqtnx0eJ5sBH^tUCn66zn)Cm!xrhkZJ^M}>qO!f$=WX`$dz=}4EqqjyW>ll2AbcY-9deE-Pfo(w;WPo=7-{Rw%qr| zfC7Wsw4liTw%1@E(1y27#e1Z~dFhO>EQ4H+RxZ_?8hlMgdAq$^{jW4-EGz~h2tRhcEA!MCHX+Oxjc|ETaK-W2-)zh#gI za8;)YtURTid58IYRVdpu^NwS}8pB3N0mr!wHLWg^d@|5n9FGRXZ3Oj?38hgR1y9io z(UBy6atZO>C}<^HhmBaRKPEhaP&(11P(G3kp?pP~B_Rai?!av6fYX?mh0@Ay`G+tf zn)YC);8epQVT6kE$RUqj^`%}}37-jpD#$f&Dcx;B=)KO@xxw))VhAJ&d~0M+b1@&n zgp?Pfw0{B`@RPj8R-R%rJydwhBp~h}@+)!AL19hQ7s$au;Qa+~JP2Hg8k8D^f;?rc z$4HXos6;|4o|L6-i?k6h6f7IdOhMb%l7PJ%UvS;{Gqy-VO==<>~?aNnwAh z-3u>W2)3|sknRvo@e-rcmq}&Asne}=>f~;Wj%cUmeDiSy4L$@7zO3sqE^tCeoJigp z)gI{sZf>!^+Wr}&RJkbikzi7NQs+^>R&79=Lp1uR`7=nc@~HRhbUjSG>IsbRXq5)_ z7BXw7ZW7LfJMA$pOvxUvt}j-hl@2%x?gQs7UcYm~`Qjk(U8ggaZIFxTE$-+Y7tW&u zcN!=s=Z#$0Az>_DVc?9EqaWk6owww)JtC(qXFtYgZ_9l4c9e_HV3YX_Hkr@fmHF&l zF%@97c^;*?Y;gVQqFBvUJGY4Eg@mkp(5+yT@T|d_Zmxi9^A8OMyJ*&<=G}qLt>fBPiZqYaiZ)#005!k#@J%@}h z5x%7-~TrWK!4w>H`5ek??dTA1>gb@gDLQDZ> zD>Ul@4DJQA-bfKT2;bfCJz_q>;X58W5pHAe^{w{9m`-d*Cqb$fUfgN1YecE4{V|@T z^<3(IgAXz7s)J1XJIEPa2eUiAN5Z}lb_48ju*>x$)_44AVNmhQm;da+!bt|kw!*qy z>WV+5P7^dfkh$*U29eazG41Mp9+&kV7sdP0z5(+4G`u%<)V$8Bc#pGmJ1iP4!G+jj zv=IXn8q3jl8~Vj>Fyn);cy2?Ayh$ImSf_h@Qf92##)_)xfD5bV`X@FaSY_b~<~ zj``Hd!ah8=Jm$2pX8Rs<@1sFz`NV#ayVfsymyhPrFIoVx{HEA%ILG2mE?okpR^|i- zQNYM=TS!#=hp;0>KgiQ>mF{-ET(9wdF(etXuzSV}_D7r_2^SvwNNAW;p76QcS58G= zNqlJC8$B+1JS4nCt5ahvrxs{%do0G1ni_&JNQ`BB>b3=RpZ$<=4c2`lA$_Emky^wv zsmD%4hxrIX4o1isp(7ETcL(_eeA1mCnoZcUp zQM#@4MPWErWb%N~ApMKNP=p+5yj$cCs#~Ir5sZaeA(M^do?0+M*xalQ&BAMc0;I@m zd6IrgYfx$Bs%3ikj&*t_7|T_|xW4L>ncaNjUlQL@?6b^>z4=fli*QtPgBx+T$Q!kt zsG&_s#Hip9dt8`>SD86n)h>Zei!+_H$4==>V<;icBcT+nhpmM(-eK#5nbJ#`1#01P z7%pmCFStD8Y{UJC@bNt%qjvM7EL`3HkdSCb3xid{fc}2fOZN4_dbPuThhw;{daIG*MCyp*x&s;#K zhw5z+3Fi@p=3ie56oULeuXAABXv9+>E?QDu3W#{V5>i^4S%Y-0uoUeTjj6nuj@dEK z*gl-zPn{LXuGFnQ{arxAY?jXQ*$Va$`#pP#ZD+5tvnu7^UbdfA-Q=e9|CCiTh$rMa zay-|EQ~F5uf!ru=8aInegI^}}L@`V@SH!L2)^j_!H@T0w&*0|dd{muP-EK%*f?!Z( zsPaYkHL8uOCsjLO|EucnDy=B-xB>m`SMOzubY1FrZP~(Qw9fiET9C~+>4(yA;)GPK zHlU!!YE^1?)>@rn$(HOs!T~cHh51dqxVwq(ZPva5UWvJ}pWvs$& z?&7$a3w*3yb;;JQz8kC|9j;n6;eoYPDLVy@;g!IJ2X<{4vm0l>=wzwG{%7I!y8S6L zN_Ezmr6IZ$D@nR1`FMCcqj`JXU$AC5%;;m~!mo-kR1r^}z@1n~!RIMW{hNt9Qy{%g zTy4_zt`V%Uds^&`I|RY>xKI#VBV%le7w zF)0^UO}Hb>p^yGU`o=-#`kTlFKNHeja8D3@xCyDM^+bKVOW5}Pw;jWL ztaypX3b`0obvXHQspRTB^9-mGvO&!(+(2w= z`P;YSlXnRl04=r+C)sdEjcRltj#|Gx3vgyNnQPI8RRjCHZn*WhNR-g?UVnRx=eI4!>v(Hr%Zdi_@Rx6O$Z>LW#4@b$wrCSG zArGMjw9z@Q_YYe@H=;EUH6@CRTz&~ii+vnW+UA5gE_H4luHRQhPedr+#wznbUqU%Yo z&=-PeJa$fSbfZz++SXOO9ztv%-9Brfm4*E&^l-OX-L~!8VxD?J6E$q=`duF3FVqc3 zYOgy!sq<-Asp9&20~YJ3u_Yx4-RQlwFX{#*UaH%7S=G_Q7MoDp9TEy!n4tzP>W+S5 zSl{X=FwPlX&cY)T9}K5v+8DHM2v2or*{S0mdkS)$P4I)vY!O~AT+%@@FL9J z(4RpQPc*>vX@eb_-X_q~9tgc_g*g^UefP#)C+1Ib+G=lj|2jl`s~bn{D`)- zMA8|U9+=xDaO!V0R7n2X>Es>~kGpit*v`Vl<)(ECZyufOPAMB~JJvEYHom`(Vswtm>I=knQ?$?U`lV@D7n9L>a=`E(3=T?>Ssn8`)Q- zSt%Lgs3*t1VYGiAv_h(gN`He#lehJ=y01!Sj~y+$jW%em+=X3*ZLP?U;Zyic8>^H3 zbnsgaDOY97G?AVsxZ!G?{h}9)I(6@va49mWTEfn=_N&qfi5fYM20XQ-bu`Y0nhY-k zT2kWm)Gy;ZA<1#MwH!LM?d^EU79Xi8mPG$zkuACX+-E9A|b&K_+)uW*!`1M5V zE_2Z0`xdJ&Yt)x)UzJA7@uLlW6AuI8;6zW^#nW&A-_eQ`-^O?7*rBrjP=lt)Xf3Uy zmp)WhvsGnyrS8s@xuuYn5xS-B>C!G^Ree`B4u11wX`mb<5EO9mx$yJo>tF5F*Q;97 zH_&#nv}CmE;)Yv zt_7Jd3Dfn9D=4k3Op@OmiXDHF-;7(_Xcvk>Nfs&RI2bt&u0h*E?F+eKjihu=>iuad z@g5DiNcjHKb0_enw^atVay?%AA~|9Gt`|jFsYB4NPKBOBZ7kYbE6mTAfDgfd#CuwF z*M)F%@u+rh9B;QDYk_PUahHw0-rpzeEyQbVberp&;$>r(joU0(RFM`v#^9$GYO6O3 zb3$3)`yp2~TP2w?-esx`F{Cfz;3* zUXd0pZuXDO3t8Y7$KeeM`1wa#r1u##)yITK{Ste`nqy<>y_jRdmbAB;*Z#278?q>b zDA0Hg_-9Mh9*l`+bfdoiAii*4M9`O$tkKy>DLb0vpB{vX6SH_n}HKT<_fNOm&Rx>=Y#mkk(c!+UcA z(l)S&K*+gMg*|PKsfh;fy+;^vg8gXQKz#iLUj@-&E43;eLPraxF%0-=mg33R$cwoZ)Y?*EUC57*G2?n}r$1;k%=I55b7zV~1d`Ld`gA z3`$f*u7S+1HNqhtbPMH>!qm`XBo{4)?9cEA5vm1yJSYRKSJQ3oHL!W0{&h_g_D71d zCl*mtjo0QXnTa#G=JzjvFFt}Mxj)VJ`2A`~;Vw1CHCojlz{D&cvQJM7#%jnWXlDj> ztuAHe^tg$dD0+Ls9Q3sLhZ^r>6-ljyG%K%^r_GKUye2MK$Iw`NMLRWu(l~+A%l;mD ztz!&p*JEh^1g9_Uk%Arll3@2}vD;_1W46~EBd)13aDika39W+tcR~iJtZ_bt{w}0q zF6?csZFOldn_E-yYj1@QW{W;;_v5R9-iF718qN>nJ@}CIKIK@;L7+F02co_auJ?>P zm33N3T?PFZ^b5q3m}jR>Olr3GXbr-c$Al6amja2#(I1}M1o`MJKxg>y+~xqg{hM~2 zFaQjK7J@Y)vY~Ya1T#`EZ?)r{GVBA!PThAsH@vqUZ+cyibFhg`WZv8$yZzM`+%}nW z&URzi=l&(E-M+bn8&Pkw)jcI_j@q{SDey|kzBYR~=7_2Lo)Tt7r|kX=^&&8`=gE_3 zH^g=u($A^g7VJBOAk_1d4#;DMncL#9m$lOC+=)h=_*Tq^=Q?9`GT_DROu_z!a6-@< z>Gg`kFpZV;6()ae65nW|@@xR#KyI%#33@U#wLLW7I`Os#^Z5*8K`MXn5A}&3E`gm> zsWl#+URqy$U=VLKeFNqoL0o`!|&l-BnOJK^JUCpa!U;X`(c`_Gu`_EWN*;5@GgPcmR!ah3SH@2&P4KrqVP2o0E+BNe)~U72cm_n+V5m+MJGu=D$~m4_b;X|!hz{X z|5VsVLm4{{xc;4gI&9({2n(40DEM2!4IjvO;)x+Ykh~_wT;Gr2n~K`?U5jt36Rz)W zcD$*i__n~GSTC@@1n)?r6yNpDH{pJs!ry{#g75N&%qr2pwFl+{aL>Mkc{5DccLTo3 zeGf1B_xoOo@A@vrH~BYqXFTCyM0lz?Bf3f9zq=^m*RQLDUopN3pQf9X*G=l+*Au=G z22NY`{MnLtk5NyZK9~8%!sUa{&KdvF+8z78sNb_-$e!Mz-+#4FC|;BF(Q`c_XLZ6k z8NhuPYeSU3K~YlrwV)TdUx;QrxnICH(K`yd1iBFVS?I0M&CoAHmqUk#OYs{>x+%VS zWXc3U-Jl1;-UxFE%%#x7ptD1z@JIel_?HoG6ZCNC&wEPY120SP_W*yQ%hU@Jey;B| z^0%);%mr+87^en_0ad-!?b&Inf9TXFlR7r7x+1sGi@KZKLNc8 zdN$lr0SC!IeFM&C%&-XYr!wvPfYAh<2D@K2`d5S{I2)k@VDAK7F^OsSL5H{zPK2DV z3jq>7TPv`h2V6g106qskmw}Hn2ly-UbrJarMi|29C76UyyaeZ3zCM1NX>Eu*4safW zeiio7fb*aN&aLt_ZS>?RNfXAi_McE}NscuycWG9BR&Gva29hnv%rD5wnI9F6POa?a ztry@lwJ#)5z&v(ED&xio>07`z)v-O1()Wt1lK$@B35Y;(S zwSvUokH7?5v2d8guX>qpG-w|J@tqBKD)>D}+Z*PK;8VgO5%skrxg&fBn6m&o04Blh z0&_FMQ+sn5?jbOrsh4nhr4ehg_&$L8gyxX>0uGu+HVAP1V7`m^Fs`XX*KCZnE9@>S zwUIm8+C{Hws;E%a8>DaP>-z6uSbuKVet=7OQXY$;N!AXWSS&P^&FxUiy!;e=WnO+N ztkR$Qmk1PeSI}xY3O;sv{l=1F|n?D?2F#Q&lXukFA!?BEQH=z}Sc{ujzty}MYdTX$# zmF^cNy?yuFJsuLZ66P~Pa+CaBbR{zten(_?$v1xVwEk&PcMVQUi;C(WKiYf`azHpy zdrM?=8hE9M81(K$on6#<)Uqz5Y89^d&_eE<4rPNwPcj%M)c9 zi(zED7)G{J7#CS6OfJ${1pURhTc?=<*!?lqsdx)#&$csdChW5jb{q5q&;Y@X(hn5D zevf{j$it$*Wr4!|$HH%r!&93|cFGpn=dS~fdodP!4tP$)*bq2O8q2i%VZs|?p6FLf z^C?A|&%=#iP$1>nLZ~AA0EJx%CsN@~b{CujwSG!E8{~YD zr$JFhp$dDL!pL^uk3`jVsU-cteR*Whq-To?SGi@z&8;eXApz?o>` za{oCnsojV`1k%cJ)d+mkc#9OsBk~K$bd-}4-GAv%JH{CVI|2H?mFB(RXKp9z(KeiU z1wVa=dfo}LP90pEe-=Yk|%i#%*L1EV^ z`mXhgw06j0h)p&s+)EU3UGqtT`EzjS0QwU=CH>(f)u5@`ZcFWZT1lrKtqcOugcegos7m$6?5KDamr{SDkchPfW!)Vh@*94UfB zc8W{(wK7r2zCmICtD=nlp|GD&*a;4)n_;>O&d{5;uzg#ghF&^bYPl8MR2=NFhDxI$YracRLALz63=#ODKFh3C^5>Pn;Eu0Z78h1Sjljg~V zD5UZ@tnEGwK6@PFRK))j^Nh359}^6ykqZBjxA0eT-~}MIUY@JG3iEYnf}vD|yK6qP z68^WELs1x_Bhi%XXh}>rXtmBqhxM&r>PH}dOlj3xMsKuh)JZv61zBm?Sw&d2!91Wr=Y3{LBJ=NlyL}D{w4;6Gf+t4CC39iAlrwsng+| zxy+iGPQLjCX$49zB1P_!0v*f9ERaH^r{y6Jc{!Q+`TPS}1r|ORDdiv<7Q=ZaB@g_0 zDFU*2EjaRk#G~jZntoy!AvY)Ann{=op^0)4iJmDlM}8Uw%?G8j@=+H5;ygMpFE?)(pPx1_v!GBc6)FX}yaw^8+P2^I^S1iW?d}3P8d_>i;{&#~9z)T{X+}wif+_a3$ zyc^x~!B#GJ^R+M{BrvJSVJN2T%rp@SG0Ws-{Ng2Vi1$nvIt}2I0P7IIo=5ulj zc!}o~q0@5aSpFi^&$hiQ*6T2G1T}B zV`7_>Q!ob~#yM%(sCP0iq0E`6v0-!vT>`u7h3OZ0q5zxJE;U;16^M8LUH;GpNYvp#k#wGt9esEuZ{98Si`1dz{ zkm&FmKS*?-@dEWf-+U#VhbC7&A8y*r42~S(lJuxABX|>y-DM-dMY}JKgHk`4g-yNmdTa^y>ysKK(7F19-9Xw z$z=??2d?pmF`g}l3%L!(Fl{JMPG|iQ>Mq#l!8QoyQ23GCAoxYY77aUv{~gT1h>?cO zXCl;tfJZ|d#t{xKbu)&N`|ru%JP>XTS)l(DoSB=kCKOcofub=hDXNR^P}MRJT1(@w zmgX+F+hq#78{g8K=-{rgdofK8q1=scvF18lB8j{3Rh}iKa_b=_^Q(V%$q?L42Rf@t zir~7F&pZ`;MsVFlU;Z}1HQXk+@7=_|QZp=){8#-b&j}NK4ZjN7^~i52=s>R2?vNWf z{~F$tn+Ngyg9|RX5nMVGLF$*ZaSz?1-61z6zQ2U`I-=YNv|N1RUJTxB~+{OhiA z;}z|sJL{XWJ#%M$>qg8AmX>o!nX9{u}glXaA<8FNcd7?kYFBmqto7+_(<&8+|3Z693+gcKtW{UZ*eF+}-|m zlIw*8*L`PAl>HlrnUTB3JC8}^km%6PxvhP;dHum%>o6Kc|3;{1$oT(SBj6^u?xHW< zK+0WoNS6Ce*BYn0;C?0JOLzK65nOU3^0?f#uayH)fS;)@%i+W`+^`c|awE7d zw_gJH;BBJs$H0q}yUR_ve?jow#lM|pI=E=*F1W%?^mSLgpnIKE_AdI;8CZ9yX#^C? z1=n5mg6@^x4t%XQjep%m2T@R?f)4KDhoIZUXS$op&0Tz^l-Wak;Vym%x~bgU1y`re N!HJF>9AIGV{{y8uJTd?P diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed deleted file mode 100644 index 1842ebb06fd991928f8c0f91b040041adfc78e46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62820 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

l*gVHD>AQYh2lV*ZH#NAe|Vm^I-?Ce^X|^K4w5t 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 DWczx# diff --git a/panda/board/obj/panda.elf b/panda/board/obj/panda.elf deleted file mode 100755 index 328b53ddfca3dee04b3409190e8c6237812bd02b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377424 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%0D@jPhYDNKI2fD{z$yzvkuj_?-m#P zoI{Q6rwMx4p_cSkVe|72wWTkf2Ks_So$XtZ%#|<3O&h4ZIyeYrnu<`Z*2i*S)l;egEF=uxp3%>CK|YZ^ljEQO5M^BDF_jlXz$# zD0h`_Wvxit&jk9mLnTuKE8mHmBwYK_yCiqM>(2($9udm-92)iZs(5Q}5>2U1!(SRC z&%egWX#h}TYp!+^=281BJ#EW5=QSc&>Oj8zz#bm8JLCh};Q*w#8=+n4%q728VQHl| zS9^sD6M6Jk3p3#|kd+n<|2d{kWqB?+m5l4|tqkRA&uh$H8OdHrU$ciiquDFo$1#V} z_U)1p;||rQm1E?baHugoE;3)^P)quV#LrrX+MLa<%Nd3{(}V_B*88(k`d#5t?C=_nrtd-*uT=cmdFkk3pq&m~ zl)gwgPxTruO<$tfu*;ua)ouf_+o9{yS1RX>=e#Mse*@60Kf5jcotWz$hn}APxpMCH z+V4v5R6FAB=78uAA!XXR>N*UXSh3Lf@M zQM=~k*$(85evUYhw(k}XIqFb-dZz^9ISw_ZKivg%u0t*9%hcS*JJgmQ5kk&$s59Mj z1n7K+O6_Nf&0XM7U;7!V;X;R2q?byHUgXeVdKeYIa9r!>V-BrJTSQ=&IJ6=C z8a%RcsY8?L8&&{a=FoI{^M0TwIJ6`Et>oDg9opq+y*yVhF})`p)PP^%&-SN3&H-KN z(807tgqv`9VC0^~&dFk>p&@~QSlzv0?UF*=L>E}h5*Ew`~dO&RR zdWWt`KPMgL28XUokDt2tHvNl3 z`Sgi;3}BDHypx5g$%#xKx2F5RG>#4YDs4=Amg_jYD-TS^lgVa(^rc`zvED; zefz$HNCb!a+V`J<%=w-}E84dnpv-@DXt4c&*!1@u8ci=0!T!LZHR+{-e(2DK^a?>g za%eKWM$nHPnogf3viLWLcC_zMnLlx8SNd^H$e%j2r~Nb@{mg;=(eT~ko&TOocG7xO zi~q>g{y?}B4*t_KIaBzp1NruCYW(jULJVl!qr~kAA9uDbOJ~QpKQIwhat^hm_nk*Y zi9@IspPK^AC&}&9cJ*{z>EV%L*P%4M;bNdZhw9Ux zh+O&|YD~`&DGfN(lD^|qpydv=rQM5xRyfp|E)lfSp;CG$=4xe?Lw)Hg$-mVOtw=wq zQU)CwOowP}Whkka{2xu9BJ2}a~H4QO4`a4BV;H}OL;)%E@?iYI;{ zp0FWlx{HT6o6Y7|M$8z@>R(yD?L-};4n!nAVH2qI;k?o1`)B5xQ@x>kftTFwY#{pFwYDs@Ai)g1q zZRs`pflhU(Gd)j3zRQtNN^_cUyS>c5^j`7B8PB;Q{rmvXtV4ror&PW@4vnVI>;u~C z(3Hcg-dWN1INE!|@*t^m<3;~_t&z>F) zKS^`!V3OPi^466qhm+b0+0##{<|CdfNPt2r07pqtPNR_A z`70L0UIwk~%4aE0C*_54V}4h2?K6?IN0XR`i<;!>jpLf=MezmKUmNXe@q{EF9sL#9 z-nH0ahW+SS(8aFS>I?DFhpBwmk_;}yNAF94+cG%BM_SN z=OJ9t3((P|mK35EHR7e*+H|OiUL3!Hq21NF{Kxbw->D}pOUUx>Y8HH9Z{$9?7OQAi z*J7oIBgGfacvRN68>Y8oYi&mYDeL3RstmTa$v{d;{x&qBw+z8a(6HwL>;f% z4OHm(H05=?kUr1Uv<^~OX9qtsO4v2KB)yn^gpo_|{<5un?$)rTua07^|7 z)P~Tddddq+Ozg(+a%!9HSvm;N63#dZ&|BI7&=yL)0Da}N0XUju44}X1Mu1ZI>&*ZI zt#<(Qg)$hLUEU@(u_D~47OYtMR{(=y`Dp+vmwpmpH2eVmJiDs(O8{%aL+ae>rQZVB z5QdnlvxAF%0x%g~%zT+0YW*$1bT|en%nmQ<06ROPj*qVad+ERp;H^{0n^8h%-@ciIrEHa=mJdG;*T5P0S!m|Y| zHehl1&y4`B2JrPoy4$zJfFs0*W?4^fP%o3=7rZr1>pHlu*d6s4=Khs-vaI%^9Eakn*Q`=9s^q}+1N#g|0&ow>$a(apodcsM$fR^3P8Ip+c7O(^BVR%CH!Fd&VV<)yW} zB-Xo~d?|(OEqC(s8vblCCRAgzfxl5DP<^otx|pk zbLc5&DGw;6bO9+nS<35`(mYB^Z_R)ZnLJ2Jr*1c~(QoNZ}bP*N`%`8}tDpPfQX)bUT; z&MwZXz8WH#dEa(;HOJZC!jo_}{p%|juA}f*RMuBxQY05%1ls#j1M=b3V6Lw=RjUi( zeayAK1qRea$pvD3_02;nEoreMAz6Kr%l+foJhzLdd`Z8R$p_~6;Zznuw3}i zE}pmm%ZDvz0t9PEAv~@bpinCkNyAHFpZzuVq(1DS=>F7z20lIlP-_4ub~0!B7Z^~C z5;dW}E_t3cs`O}FN}fp%`s-Uf8LItfSD z@x(io5C2I4%e_+}JcrR)?wu-xeTxCSQ#DbCDq3EbJe7Kql?d+e$TAIRNX}Ek4nd6J zn+;;=A-U!2q+CmP_S9r4A67~S)por!duxZ@$|L-`UYF%L_zhCt0zSImljT{VJTIFj zC!KLUL4tSF@FrMOn(_-;GPz^dDMQ=x2nA z3+NZW6Po_hv@KWs2_v5rx1NpwF1`-34pDL=R`1Y2CRlzcq4DoL9$KDpw^xWyJ?rtL ztjJQLTS!^cNXp6u(z!yi@8dM)Q)=F-EakU1ljrCWQdVav?^elg?k8n1tLx>I-0}h1 z`40Xv-LUgD+!Lp8394b|L)<5|%;=%vdgTepE5P#1ej402QfRzZ?f>Bv;UqPSS0wZ_ z+y)v)QUh|~Z7{cy+Ir>62O(`_feWVaq_Y6(EL(l}+w}nT1}qG>BjHCH?48DNw+dM3 zq9gpkac#8c|1*8mOH-_wa5=e%u7oRL6G!4QZ6}Q*_Fa zHOUt&$8S{1&(+0hN?xU$evYMhDJ{*EavC@rWOd88&z`F#{$@w?ot|;V+n!epri7Z9YU{fNYZ!>i- zN~^0neQ)CMMd_MweMf|oQCF0DeD6{HqD)QjjjI9@aZUK0+uR>YL|-`C7k!h)7db@{ z-GrEgeRX^vYVp6}?nPOyl<2*H@l=#pn>{7N<6S~#QI2)=9pctv&N76xZA%^RSqz1u z!GZZ=u{V`Pg-Xc7A(cpTyuU|~xHwRw61f>xVoyRM;CMrLYSDtgvnnw&A#pSzQQ>$$ z9)ZNwfdeYhMkHi>rsKL=itBhUAA`h`K@X76yD%ZqF(HxSc&FpeMb8BDRpN?-#L$EU z0V8!R5-$XMsl@7p#Jq$=lH;wQ_C>pclT>1ew1?QSCL!T*yzh#odxFbUVl6tnFEc3= zeQ4{yL9|L6?n&fn=6J_5uNCbNJga%uWm<`&35n~8{ymX695|p7uO}qZ8E;zuBF7si zTonBk@BsO|+Y+U8Oh{xnZU0I}qJtaASCM}uM200q?sVF&D?_Bj4fIwKzM+xV_G~G6 zteYV(qnYs|ksk?Sw#c6Z8Y$c%FXmjNdEy!&U9FPCvzG~NwrEc0%qsoEBl0!?tO zH*FK>c}76dMAyd6X9cQ@TNX{0ujNXCyCk@F>_dp*M=%xX&It5QC6?5`!6HKh36+Da zv`Cd8ZF+;;XpuKi?LEH|a4Ze%hgG*4+^+w~R4J3`97%Rd6h=dC*E z=6S!zpX=Xk^HhkI1$iKAEm9`PiVTo@Y_<9bGMjNyf1Q=tDb`G7u-9L2kzF8thttNX zUEElxC527RVZCggF*rW=JQ8*j#y6O`w2Qq#aB?J57oYb74D`O=# zs;-IleOrp%6$SBxWU=V;4j0oVSAcl!9Jp6xcD4iYS>yvjwx0pwx5&GKoK6+HCfh2$ zBglBsoMOB8J&>w*7?j=ok$+I1HEvQ>Cqh-XWOGJHrpI@{)qnGx+!wg^-p!3~cYmtj zKJR*2c(h@qJ6a^KU$o1v2j4AG|i9FwHjY;&?M7}TYY!dx6(cIT|B#Hj2y1@4< zA*y(Q?wAz%UX*gGqOut01ykweLD|yCMZR7>5`#5S<$E4qFFs4PP4ZosL}G~Mnd;j` zU@9J}+NS$Txq&DiriqK4oL|L+;ocZ^$dy*q@YonNbyJxKI<#ot#%i_cU)?Y|9k`yJQwm50p@6<2DzooOPQSny_v&Qxhq^ zcckxTX~k20S1^|s&$gRx-%cPgM>!GS*P}_yHC^oFl-4CR&-;;-Dus2)EuJ5cmiA31 zW)?5dC7hADk`@ihV2w_p`@zEiw3?xMros1{$FDrR-(Oe6mZZiiQ{ z$mh(;L&-Aln_PE^6)z7|A>jLqB}?)3sdDZ3F2V(hSJ)~%E#7=X+E6$tzQru4idUK{ zuzuz98vOkn2h15=2xpZy9VB-oe{%jg6yT=JOH^bLf2-xLeFYc2cL#*6wjsy=5=HP?4Ui&U_Lzts}<28*y<=pUy` zUh$oQ7r=D0w)n10F@^hgLf+jWDK)9pEUwpvq>z+W6X%Y1itkA+Ad%|ZO}H+;H(j=T zW4&2)>*?ZsR_3_K#KfsTvdGthjO0eS_+yLw zEXaKE&QC1ziy+@fgxhbCUj^wTk^EDOoDk#{asAIM@|hq>G->hY7Fi%jU1o;j18HVX z>3)bgxz{+5FRaW`ky*yXSp20$4hphEg6&tfUdsh(KL+HWMGgpZNXj~7a~%=nW2x6+ zE0ZLx5yS0@zqLq;AgpJc;_obyD+t5fDgLKL@&suqBmaoCx!^Tyo+5$#dyDL!gUrrm zAV1hWzf8&XEo+V9A1#t4$m4k+$E=0V%2IBfIKfX==BU^_htXU7i$zi;S38N9zgl}@ zg4`@)`h+b#6?@t)?+?Nofa1a2&Y`GnJ#Kc2qpsEThd})zKxuAFrZ%6~k>$fht?}Qw znR{?KpZCBCko+h}qnJF=VC^c%MfE@$TeHWAkz0tidaMb1UwPJS*s)FZ4d@J3(t z5pW&v5{5?LeCy-m#SdFi*T5v}nUe$=NC*l{w#XDgx+H^4vB+dW_A?9u7g*#1K^|kS z3QV=gR6#ByE(9*L$TX0Isnh(6m@1>!4--?5Ff?7qyS*jC(;AJ3;q#6~xb3kx$RDm9 zAsifX)?=ScWaR#$36VcI zJaE6}$%@eIXX63E^K71~AjlrkJJ!miq=HnicneOpL2S6xC0nNZkvTK;z+g#$n`ZKvf) zuAWRqp}0kI1sO~;hw9p}b>vsuKMims?MBuU5w5)l!iY792u`3EF^NiNB*grVcO>IJ z)HrauN*rlVJvy>%3KiJi=-3XVC2bb!XzMyt>Y6G>pKg&Ef^4EaLY*x#OOS6fK@M5Z z-$~itr?DnAQTeG7HGj_qNwt+<{5i;RW}wsziwqH@8xbY7o<;TvaxZZ*wUI?O3bHH@ zqboYYd=c}0RW zXbw_t{XM@et+$Zilv-({W=D~ED+)5uBJBl97Np7|g@SaTH&X{$q>Uh7;+3g`Em9!J zGE7T7%OWiWxs=%~b%;fp3lhTLQ_r?YQ$aQj02ykLd=MKNQ-}LEbR#G>u9o0F=~^f^1BEK;Y_1EP95-cgcdN5gtSvI$W9hPv5nS}RBuo>=0yNR8%d z1d?o#+XOlPY>*U-tQO>}e2{=eRtoZ;dLSXYXSH0AJLuJtR2zAhf>>uMiTHmj#!a&E zCMS9Zt7SKOkhQ8OI*y5+H9?U7N)l%*K9u@Db}mR=+amh}*+*C}$+F0PK~hJ6B+g-c zB*@n|QAuL2|6@TuX9_RLvAI4G4~T2{2He_;ip`+%k;FQ2 zNK24nB15Fo${cSF(j*1sY>TXJ2J#t8q{s-{K%de;Z3i$HL&-i+p%ogiQGU=oeY zPLPk|xkMkk6SQy;iPJQZ>U)Zz9nIH7%(qh#O*E0|Yma@=rkcp|UB%FeHq!*RVuknM7noT>42yQQ$Pz(z(3jD2i)ZZvp~M&YqisVlq%uzlVm5>Km{K%-aI zxmc0IAoY`^5!$w4s73P?qmhksNLE2P;B)W3Pk04W-KVwJ-V<#|tvuB9jE^M4!hJOS-FM>>sKNGSfQy5^?qwv{tNPqVjzt+iu24 zTEer(Q!yQ+58|*oNO@oRcCNjT5zNwZB7*ZNf64eIwM}beZ{3d9r@U$DAkA&iED)J9 zsDD}uYr)lG(<2PGwE1>fl`1mlac7erwF7CNwCwg#AoZ+g9sV9<2Tq=zYg?pyE_&+I zYU!6*J-bDw4K16IsK`;7L&o+33EMesvB*46G|Z@LXE2Ue^1!1DNDFK80+E?cO*2|q zo39a@8|H!BW6K&TGOd^l1|-?mA1+8g+IoQB=2|Jau3`Z>An|Zzl^|zh^MDj9bE6G*iU=xu`RrGerJqn8U(H2|cAT{SKdJ!@&8cq@wx5#(pqk?}URd$z{K zVF_PxKJ0JydOC{GDwe=?T-#%Xf@~x1)XB3lF+ujDx~^+S;tn||AU6&R&#+F7KkE}= zUtk)L&pVQ$yf0yL-4<3$jhq@tZNS$$I@^a~-%0!2$2S<&a+`DAmVoAzwsh+W!{_b8hE3ldOi?A3NqNt(Zu31yFsL+V z4jo@7geuLM!zACwK6x%>51YiOPi2ZGQhiz6ZB_;}5%b+le5o{NQZjv?^F_~26DyK+K(^b}2P8U5*SJ^-l z-F&^6JSrQiSh3Tktn#!#Hbd37lK4@Xudw60Rhp)WwubBbQYzC_IX>S_?z*+vTu z`{wl^(N+^_zH1mxm4%v!``%{KtZb)=Y~Mj?jrN+T@4JWFgUSw?XyjWgjn+{UO?>AP z?JG~$258}1CSBc06RmtBSP)irRtsAD`fy)RS)_?JzD;6v7frPF4VBL6s@e*DuL!4` zCOZ0_B4ku{*F=%;Ra&yLSQFiyoMIfgvcxNX+$C1#2F6J{cW>?RmV5jRFTLga0#nMo znn?29MC2_?oVERwmMQbaBugsKROI-6&J;o+Unp+^mOGh0C`}0*5v3I+aLlLWzTwmS<5~1L32B$}xq0D%Ek~Tw`@%R*NhO&r05KGl&D2r+$tj$ms(?rC{`H10I zmgb#Lq232ZbDd$gxF3)W^&ksO|+`|h&iAvC$%GuSoOAC=bG!(rd&5P*GEQjofnp8 zS>8oKu1~Y`K~sr$sm(z0ZK=&cCeK2B3qH}f7ZWBgCef0_MoBCu!4E}w4-gh7-$tUq z@ADRrXOZ+vQUs4Q=Lb3bDe<|ihx-Roa@3lV0?a%h(qlgFAE+?7DiXQwb{%grA*-yX z+a8R4|8vQ8v`7(?Yc09FEm=*Jm0Ovr*Iedv+#1BlY!;dGq@~WVGJiGzBePRv z4iiPodRdtyIc7LP^z^bady&aJh#^AS16~|Z@_R2AU1x^gQ!7*D(2jVYylY|QyqrID z${vdRp%T@isSR8q+n4o|)&|kkpStw$MH;|hH7|1VOmbwcT%zX>5tVxQ1HDx4s94{S z*6f*VxA%_H2l@e*m5H3`?Ei4>O=l1cbfbd%yyKY!yHu4&1N%s}nm%iWQy$Yn>iBjM z(96>_k)$uwl&6P8D#cNY4s)rt^mcOoAaa#wc<%@D&SM;u$5Vw(pNc>I)e@x0BK^gS z#W`EBSTL^foKw}Fnpe1~Szz9ClldIiP4;$nH{xwS=nK-qwa{i}={wxJtHJnnDp4h$ z=Q~07x;2}b?BdlQJ1GUghtL)~T!GxRZe4j6><5&8g;S8&2-kJ1+(OqK)4?_s zxXw<4d!{2dAMCwTJD#lr7sff_FAGliVe0)-pE+F7if>4eZjU{6O} z$FrG>VV^n9AS1xJ(?4-M&khSuO2F;@5_p$|7bV~`6@Jaaww_F4WD z34YVxh5se+gu&F~2p6w}sK-$*xPx*$C%AYS*0&~yah^h_FhVn(uo%9SMe?r~xO>4J zO~+9`&&8-Lf^#s6%I;je^#M)Oi;E&7SF5fHtiQ#yY51h!P%vqcaR&E!$7y9o06!zG z|1uYghfpi&&~LE%T3h%mu;0MMxn*%{Jt_1j7){}XvUs$4Q!?YH-#+n!mac!-Y$t_r z!+<(OhR_=%=@}=qmka6Pop?sF#cfzLf&6Gq7g{s^)`9&#g09qNzPg!N;EGI8Ru-om03H z=%s|16S#|{w5PP&T&yabPxTI+6u*V!ahq)Kgw~VPet3wB%W#4;N|o-PLj&#v6U#zP zQ7TfQd%1YJnd=1ha*=tNt%uxAz$`<&N082~;G*NZAbq%aI~Alq7ZopJ&d@Je9Jar< zQ4H72B<VF_Q^8S) zaBYt+w&L=1Se>Hq4-tO=cuUxIHvCQhZ2EI6oF{5gr|}DauH*5{+t8UJ7$RP@#B1_u6#Yt{}GdAMIU`BH-rv2xtJRme=fDD*q$!)dS4iQ zASZh-+=t<&dqn9^vAZ$Y|59(RkB2)VFdC^*P@wvCC*_(}W>8Ou5wG(G9}D8*quShm zoRa%LntL`mXG%HoB}h{@=Qz^MXH1|z&D@BDE+_s1EdF;|4rS<%9wRlVFEzNHjMqYG zqsJ=N8ksGmH$zT5caM|OfQto^YX%Zmxp8T-*Wtbbh4K-YbbuCX&xMSJ3y@Uz4*rJ% z14Y2U0vjh0I{=!Z@mA!Fi4R*7vyakBPu9Xu8aQ+dhTnn*IPH$b7$&dD5<~vdub2}V zTaW7w+{wxpl_-TDml@=E8UuQLuJell#oI%jz>a`uw@n-cM4WxvAHL)&^|DU*r8)w^f;GBi_zWBjCGtlRZ6E~&ot`-&)J;QsnsA~ zYHa>r4PAFIztR(0kA#e6&n_$a-QPubUSdSc9M2vk!Y??^pH^ZHg?k4t<&RCJL++x%&oY9h!J7k$qfC!*YRR?xf2?KgamWX zLN4wY$H=szCU#s*hQTr=hVF&0lhIRLsBi7(LZZ!^Hpg65KF68mGG5iiVbP@ho4h2(9KqrdrPfTu8&TVRWhs-={6`DDVk# zc%HKS$^?Ih@|WS!Vy|Zh7gDjghHrynZN)tK{6LQAUy;b4CqpFE5?GcE5+3Cx{`yH0 zWs}cfcvW=u|H3cYwTI`{w5H7?@&$p4uC3(yM+Clw8puiTimqKHj3qa=+;f6 zn>#dn5(6uj9O21MMM+%b`XEyV1@ef$60W7ea1PFg0Z5ApKyP4;yoKNBlXS}MpfOl} ziw`rgNW7rI_%Vc;4t4dEJ{oV4(m^`fKsw#?HS#hRuRvO~vX}@Q1xv&^)Bl&j8|)Sl z?!6flW$qUp#P(}ZU(^tf6tGruB>4jdTOQ7)di)K3I zW=m=*>G@E+0#!0IrlM#t4^*A*7h2!Z!J9I4iYKm$zT@cvng^TBL8D);OkmLWNeG< zB@_|b3;YcY>@*t199~{meKeDv*#+j-$jH`FQ(m{et_iTOI}{0a7wH1Ado1bmAkz<7 zI_EDZy%>r&MU@P|=UAQk*Yzb6CxpoK5zG%00)7OpKp@P_TAo{9_($Q%2eQLVtQJa7 zqg=@GbV8|g;5zb1BWMSD=dAZiwV12|l~$mZnLJxs=rsH_V{JV=A`K5OZ<1y_qEGfV zu)b8)bVoDo!Xk?52zi!RWibS56*3NOdwU+T{FcfeWI2~&#;*T1BeV)Laxdj80Y$m+ zZ-l?=e53Ue^hsX+HGPQ%NY8-Zm5Dp6oOstW(6A$n^saIp(gj|A`fzH~GaS?fiqq)q ze(W6W)aAeYqHZq+wp-JjaTRIOR5<$j;t1+96q+fkn;y@rBBx;aOC}`wzb!#>Pr7Vh zU53=9Qu{S9+l=D^RZ@GJ*RvMv@eg@+V=?i%4PYIajI9jr+yxiu@vL8dG+sK`t*Jyj zC_fs1g^`mR&E%(tAoYxYIW`-qSt2D>c)wb;snmkHNbT^;FWn}ot|yTylJW%N_^D;| z*wMIm4^U4~Vo4s`gV-Y)oaC1oay_2%%MaR{f6vx|LR(EyjT$w z>k{UORLF8?^J8fR4GWTOPEuL z)FJB9<8lAuT61m`DX}hLjz~r8Bem6^Rcnqg)f|C5PJ+>6t1-t3ih&7p(BLFrCf%^u zFTb3lKx(^xk5xI&*Dyg~XkC?kdT*2vrA zIy=tF{O9AvJ#Le8(#}`Nd=AB*dXaeo;PElYcVkuGV@++ebEv2c^sC^V9D1vIIUYZp zTM5UvTh6K~Cy)-72z;xmWH`y78q7W*E=wsQ|EiT|pn0}{`7d{zPc1eVtSLG^2K(13 za^1mRV*>NhBw_`53;Td&o3zW%N6BVmpbBh>8O~hp3iT>Jh`ROu&^*Fn(ge(3fFG5R?5`ST>@5n0HbXG|nQR`#9 z;X*-iFJX!$7faS(nvk`gzv>w$-8=RvxerC%i}xD&6it~O}{t1 zshhI&^Uf}7r2mk!bY@QlPZtSy(B-bI1<8M=G=w`iO`>F3o~!1ZMxKUHMiFyG+GMA! zksA{+`5<3UNY+jAs>7@WbW&M>jkB{++D&?nRIWVO>L&e5qP67fc0U1|M2W`lu6sI@ z;zwg8wGYerk9HD|Z5Z#Rtfr!3G9^ub0*8@#PpsB^wkmq0QYz@U{%vsbG`H|gZpY1+ z+$+hsOmgoznOg$7(`ZS6%qMg&em%TQ%`b~KU7(ho&?WOuvhIL%5mt6WUE>hxFCf2s zsjKYY8Uli3_d^+3G!?7nvfuQz(+)r_M6=UKKB8ZCR4Y4@;`(TDGQ=9lTcc%1TZz}L zM0PpknTW_x#??{;sISk9yW}~ZSr)!K0m~C1ZjdbeL;~I^aA2v0Re?P7k-6H zhsvE-OLda_bdVKYl{p_!)|M8p>!+e@Wjw zsb2g((A!Yx4{}PLKoT>AtUqKR&qWmzC~(;={us;oJk>3I(%@pSdh+P(1lf;6?dG(0 z9TD=_zvQ~r+io#vQ&97lN;_q1yp0P$boqJKmq{Q?_4%Z`5XB`ZUfNQwnyLBMYMfz^ z&8A<@>UBEO<`}LkD=3j3n=NK!J=;Q9ZRK4&lXIL_JiT*k@bN~=S!-B!msP0)x+iS@ zs;rawtC~yqoBV?`ztiuVAQ9tfkALq2i3Vsm3FIG571l3xxm8Zrsb!?E^r-IRYZ0Zc zZq48e7>n%~)vy|g8?|DEpA9vficbKZE+c;fz`X+An5WOi2zfx-K>@G;G^ysYn8eBOh9ec45aFqeS1o%wA z7GmFO112+eT_~1KLzdobK#6^YQ1)$Uj`PM8xaT#7TjL2ym8Qd)Dp|IV1-#RMs{vLB zc!VfXdba_e2Y61vEz($P4frv@drpZ%YmM?f!L#o$et4Lo5Wt2&VQ-q%feD1%5KPLSig#Jm0 zk5K)i=Q*VtjKTG3%WNp-?MoE_p(`0K5n}p04-uD0I5xY)6^vSy|@&`OK!g9%a#5#2GlAp}iB>S#mm_hww8grxBTq*4~u!hDB=;XSS3> zcy>y+crsdx_ST}cwP<77&Km-!nHe(EZ*fu<;JR+jcNa1cnlR8|H5MNzq|AJ%;dj!M z%{-k1TmrCIKpis8JVRy3>;PB~nIY5MjE=nxU_fcJW;&%U%-0QhRv09$64CYM594F6p&+@LCkM8C#$JnLN z7^&fWt>ICr;TTUtt=;4B{sC$2_HgkAgu)ew%4D$)g*qgsnecfC_7{+jUoaUI$&Fa~ zLQb~Kv+pNzOgH1G0i1e}<^Yo%y+~I`jw@W*9$&2;w^;0I;YoA4P8h{iW8;@^+?0s= z`P1+=S)Rk{BGb$$G@Jv4-b3c-V$^kgsWt2zld}f25YVl;QgS|>sjU2tKv$CMQm9m& zH^is;bw?oFTsAp`#u zC;~?p(`;SOtwIL}+oTP5fadeRTO$#8(3~+-g_Xu?#Q6)=ze7&f2a3sX$W0csIT==K z1{v#@yYns))@hSliOWqN#ME`jZ7qVWQF|J6rQ0S%_HSHW&wBNCsPwQK5Sh#2%V*0n z7PDo{?NI4aHzHO&2eut5=uc_A0Mh0aI;F?lsAT#c#y_Dnerv4sCxiK#Pz5M$JO&&$ zSToX16vM=y4c3EnHz?yaDNrWoU)_jW0=P(oWrbj|TS?yx$-k~kq|rVyw^m8DjV3YE zG}=e5t}vcNeiPJisf>e<+-6dpUjhCFl@_rS2J9wL!yO!ZRtD{U(=g4j;xs6BSr@=h z4LAZ|2o#qR#f(vur)KYW85lv-yN(re> zHO*3bzzyo$ozo0ekWMvoxR@acnQb5BVjV=gJS=s1-n7e_RZa?P1)Ql7(cZMn^KN}H z^i!nehYn1;JnuFYuzqvSi9rqL4OAt)CEEaiGX=a;2HOknY0_A;0Hz7}%6J7k3Ai3$ zt$>q67pVjyNhJ31snTNYyx(bzHC~&BS4{m_jU(- z#enAnjDgBVOJ!d(VA(lmk$BxEX7wYn$0l~u#HD*pA})w=HbPDK7c1&sFpPfqgaa;~{J+^&sIB>3YY<3n4FXWRgA zt$^x7TimYV8P5ScE#RNh-&@?X1pEr%bI7>M&~mW$F~$Pj!l? ztW!u;s0nLV>lBY0r?`o1s~~j>i{TUr@sSDfCyY}(far#VxW%N~!ki9Bh;BDd@f>1L zL+TtB%b16th7$|?g20jg7FvstI)D{Y+w8T}A$uDFWi+sAddhf76|-$$NQ<*r#=b;x z7TAQq{r@FY(8o5-5?8{rm2`)ms)$0>d5)s5^qK#!^T@|q=HAYbFWqRI$Gr;+FVOjI zqgziLxtj)ud*5$_|$oCQun$3<2N|+)3CVQ?_@8XIY8ljaVNJbE?gN~ zhoth%UK?CJL;X8d`U!G^{*_LOWRxYfK30s-2iByAB zSXWL{UBG)N*SHzl6f>o;YfQV8ZSO;3sdd|2iI~@#MCW(Ormk@9DUrrrJ7%n3XOw7n z*=vUJg>*-0FH^WQN>f*=-E}@=?9~!w>1nZNG4&~^p_~>&UXP1?1@Ji(tIPnLW=u(G z!&C%a>B_;>(u)k5neBv`EG{u^Fx|94Ys6YW+`_{=0CR~uZsPe)=_PK4yr9Fw-0VU) zZ%Vc)PU#}E29a;{$+wFGDL7U3Tj^A3WIY&(7@!R7v_+9_Psu zT9lp#BrbOxt)}}q$7Zn<>Yc!*SSV*ZUN% zFWU-N&V#|o6tEGj#n{~_n^lKUwH9zK~n zu#5Dg*e-pmH^y>P>v#qO_g?5Y`)ftzSuQNkNH{Sv9951!Gb!8;=BN@isqqog^GGw% zO`_1`*#x1|ef|Pq)2YIYOdBX@zI+NTlh6@^T52opJ6*=DJE)+L3{so0>_>d8+X(Wr zrl&(4YvQ9tBvHy(ze{Czv-NxzO^1XUe>2j`OQYlmey{SO4ki2d#(FUyW2>mUL^S(8?dm2OJ1;0=d>h#O-DPnEsT;@j7co1uxS!x=x17b$TneV5yU`6fw7^ z!=l7MyiNyV4*Kds#+|M!k)A6f-5u&94;xCaJ6rm|9juQiN|);p90zv{q>m^{uUBjy z={bT0t0~ufmhXDf*Foddd&~#<7G?CoLTUC@Iwd{;e=U?f2{;4u!UWpY`m)GZz@I`+ z!8=p{uOXY<#oevD!Rd6gb{s)xRS9J-P}e#5vi;0qbz75K}WXp+DoRzqeZ%# zd@Jx=(!(HA*9HB!q^_o3=Ie+Hffhif{tLCPH<4ZinYx;9B|boUgJ3s{EtjhU$``Mm zhB69mm9Nk$9|ICM#jiKDLU@{gD@NmCbkO9*jN{SW1Z^eo2e0rg|Rl#lIuG2t=s{5vZ<{|0UjJlleOQYXPhwA-It4`b+ zLlJpaRytH~9WNj~0XnZ9*$C~NfRtzTdJxiS^xIho_E%47j#CX$wempTcuN1q@?7Q) zvfm2%lZl_D1GF!nA-!F&v!h^x_1l}Dll}zq6roCn)|)7j*<=oXHQ@Ig0>>b@>2Y+I znzyJuOqe~j@DW!~xI7aq?cQA6)Z3W}M+*9Q7+A3u-VUrSk&wYQQb@(G9HTNGyUcXX5F}A zi@~Nr{yXu2y8QGqL3Csn_Pij^3zRtHd7>lbNd@#AqOPlSSq>R89C&-rdcbTuS(tdpd6 z?j-pHq;;(0)B3dR${HBbr?hqaij|TshMe+eS&hWM)d1jzmoO=cAIHCKCV?PJ{5Sq> zN4ZYF6)%Z@r@5WHcNm@VEoxsb5Bv71eW_R3K8SDWBrbD41;{6z)R_3=n)Lwby^z=( zH_v9TC9GTt#Y@nHhdEzhhF_k5#CPb^yC-07h0;GJXvCjYEPeTDT)(Kxhje)`65pjq z!aS2uGLpsbYbcRk&=s!Fn|D|oj)xjkoxm(a{>RyI20Z)p*)Q{<~q zU>EYTcWUGj9hS}_Fg_{l)==Xt>A?6Tt#N+3w%8QS-?;*Ax*WiWU!+rm(|N2Qucm{{ zHIs$bXrARR7VZfmQv7;7f1m#FSgu!?v-tVxk-DVr(!=}d^7J)+x8cxIib5 z5}Rb;#LsOn1INj0(||-jJ&dphXX**&)(pDZ`d++WxpdZ-*!dY`I;+2SmcKh552@$G z2PjrVx)Y=)AL5mI@?jw9eu5osL`efxZaQgsCnQ6jV=%AA2dj@=2XrNbS-Fh6c(Fc* zpMzQXYSsg_W)&BUS$mLu0WxNlsOr;-9j6hbYvFjQnovTz6XfK{1F(2!t?fwE)>F0r ztX12&RMgHxbQWaP7O7f!&HZ-BnHtJv^%^rbV{VgcxY?FL0n%JXLO1DS61t!*lVOpW zX71*kyhnufrjCn0?qYgyrKv}-$k8~S`DSM+{4)yKn;uJ{-_03A3jP^ObX;V9s19}u%+-)zyeM;@`u`@<4?;R7GCwtAqAv<$OniodQ^v%Q zZ%&Shp@{tNm?&5dypp2*@(@1r8&!7{^@pWc87rBG)s!}+j?)y1?W-V}7&APUa$A zk>#g9H5wI*b!<>Z=hsA5##!cdC&yXlay!lr3s*){=BUo)c zwDUWZaRUqKILka!u|G*47fe=9nP!}w*@LJJspDr3QOCcJ^gU4aPSCh&QYW?hs2Y90Qm&oN5Xf?yV7TwJ#{R)zr)g?mf5pJ+|{|teM1IB ziPkK8S%OB6sRdp%W=IVz*I zR^<~)cnQ)whYa&80prf#bu!3Q$^2M={MhW}Q1*c=LiTc)ue3tR={jT{gs~24A{&9d zD2t-9;dahCAIYQ@pr6D36iT|1#IIcZ1Z9X+>K!2Ud-2iw06{u=w1m0 z3J{QURAMaJoyJ=!M|Q28yg!Ky>GO0{?8ikj!FUWHeIYR;eO^l$6`N4(XebkeeF!|7~6nWJO z>&fYT+UVp%aeE<|XwuJAM=izAyFl9e>3voAJknDj^^){{iskhY{j|$^w6{U~M0D$f zls;I!;#rtaK%S=%5C zf6JSIHVM0*qgZ29dK0acnJUtonW^F#xH?s&x3VGsEkR^LNG~)u~38X6inpn z-0Ib!N4`Ab?{ga1UTy8|q&GpH5mtW&nPi?lsecI^IowpMe+__yf;po9s#J7+rnY7c zWc>tr?zOr`|BtR0;K)fPbfwI30>9=54XuxRwGFo&YleUzey+eGR3M%R|zIDF=jSyzb5?|l)aY~^p2ZhN~n&l z(6-AP7AywFo3;B(4`lA=h z#5;$fN18U_>efd+BjC#tt zzr|#{^;G`J#z?p9C;rCnl+b=2Y-y{{g-u)4nUz@ly+3bqu`K#Hfx}`iFcCrNheVq- zei^&WQm!I|CoI&vA10ubGMdM&nSE=*{}*@;S)(wb^{nIE%Irl1CP?#X1vI zlRgj9{gasa*`m3mXF?e(@nddlV`f91>B(C}(eurH*-ezFYo6GA-E4BoR}!9LbJQ^R zv0Cn&BE2u0>IrgQnV&w8dDi_W>~6C11Zgcx8U2hxG-RgXvZsA;!seA9C+~e?SoGgI z7hg?2AN-@2NaujHj^}@b7}M|JC`0`sPk>~YaJac z;4g<)YqYF&qld_A#M|WTpN2Y%so(`I|8Q;viM+tOmZwDhX$tTGbS)i zgz>k)^bUY5${!cqvzf%Buc+=n(fbGF&l(A~Td}6Yh{X`UXsH{bx#hjk?JDv_Mp zrWJmuZH0$*&{UDBzhun&dOYIGbZBKFK2^ovyd%+Q%XDXCE}|DhI+ml?>we6Gr0;`t z%ty@w$G1qo3S|`1_B?Ki-k@VEZ8&8>1>Gs3f)f0#8-c9UM^r6fG=cnQ#=%x8){pd= zP=8SU=dj|41=>dU%0NxL!UzGv2S}}@|9i5?UK}E09`u$3+ zG_FLi>MBDnU3PJ!SL?XqLv|Pz>1(3M=BpCV1KZWu)jcfT{YpNEwI~!~`#EL&I?9+D zJC~fDZTvZp3>tqx3Top|q}0S8r$QFZk>+O0^@lSP&-^w@F%olN>FYxsDWW-4Dys*; zHXSL+9W+6rPNb#Tgtru7M=9M843_ zGI9hrUXXfxxEnpmGtvR^|J5Mayj79g~MW~h#840>t zR53S8-JqD)bJBA^muIyU4wwc(3`D*Jjc}iuD}CUwMON|a=YLss@9`QN05zeVTm8f zHMKb(qvg$p*xmezG%&Tfoa;qU*+(A0h6Z#z2T?0k@!>M3RwZ!UGGvwT-;3lCXPk$Q z`@*X^*Fp&QxGPoY4pfQDy@VaQ z0TtP(J#z)j#gOM^1jNv-YUuwyj)dKp!I)UoWbEdi#4kssBJZk~+=-Gqq@=-|43v^u zVzREjBU|)#e+$gbQql+nq@-*Fr1fi`B?l(K`CCw*VNV&(5nXk=P}p|W^)7neg8WI0 zjL1{Ef#4gBIw-K1Oh4S4FjY#p7;~}@WAK-vr)3FwUei-9?T~K``KMQdy{_13(&s{+ z!?s0UCR1%M)!qdKPFVIyR${5>G(L<8y5e*4-bzFBZ?K<&=U_1V-- zlqWytZo2LY?YaY`KNd_L&PJB#l+Y40b0OJuaUMhoTlA4@G<07Dd^wb{8CH0#6IrT3 z`hK8w0`C@hiW9juCV}oPpqB(bEb!RLvz*AXG&u`VcMSU~ko?P#30_LLSxe{z+!HFe zmwNPsFQweBrJN5u0pefY1SZ7Dbggg_g3PZ}_;l)RDm-0xiIyQeA2JobSS$P|>0boX z3eVIESC8c_0LUqCI{^)&%=+~LwCOw(8KsYRbk8bso@!|}ZW@)T*4$2`?M!(kqx7ZN z#aAcJ2#Is3F(sq)f#p9a;1NhC+7hGYE7D&;{tsHAVYHsDIzjpuk&_20C1dpCeGQ4b zAt+-pL6o*C8K)ESW}rtQo@K4CMnMn1E2Ae%e7K}XM>%nnCB9qIqf*vio!i1}#%ZdO zGn=s$!{u1HlusWy`ORidnB|mI>L}xd+L=ztK)ptHog!2e`dyhOvt(Pjq_BhNbCSozF% z8;ksKUz6uqfTy6cf6H2>p8;c+6qD$05?{jIFWL4E0UTh!UQ-zNkTJ2!6tD!~3IVrH zMDi@tTDJq-3Nacm${2K>k$(($lgO(~Vm{AkVY3rz{Ze!n-7J+cOU>1pnwu#`pM-`tHIbosfJXTMa>`qB=M%nC#ogCyt9YgE zNN9WwFVXm_hhxK6>mt|58|ER`Bn|di6f#E(Y5ahVui;608&Gs1OKnJleb_wa+(vr6 zU=r-Z7ihSCg5j|m;*l32AUssn9_ek40k}1n)T$k-yA~fa$o4=+?J%{j?Q|w|$f!L> z)mD=32|0ONTcftO&LO)|Ynp_w5t!K^+`FSL<`KO^RA@{wkJUdX{V5cd$tB!Z)&55M z801vU2(ww&+yu~_y?7T<16Z90Z2*O(CmoAeh7>{?KvVmh06HIF2IN$Horji)37}Ka z!U&|p_KFsXq_Fhesh1Jn0_lAVU<>I+d*6~8wf8OYc_@g$xSH&-vx(@bnXY=q0G84H zAg5p%BKVsV8=3<<*RUtSVy}+J8&YI_16~7gxq$L`Bej76p96RXqFL7yMN`dDl_R`u zEVqKyr_iYR*#^Rzkl%Z1cym=w11(5E~-o(W!h}g0OWfEI{ zL(eI(W!9mSv1KJ9e}7boRZjL|445J|-^<-$>W^yQCX_x1`TL1=N3|Y5k^WvVnRrr< zsoe6pY@WgYh=YLX^KH*DUqRVurbAY_*TT>A+ zM`qHC#8sn-!31s={p+@kZF*&g6m60SlFn1}Ex&}<0q#G{j zT169Fbj6f9Rb!N{m{Kn^@v8hvB0%T^vypk9XpoJ}&xPuqcoy*Yn0y7r%v25rm4M>YJU^_MLvCd?AQJZYL zN1xD!UeZRJ2!E`|IC(>9@X#U+!A1A+3`fV&9go^^6k4Rur*A^`DoDmr$oz2qagy>@ zla8a%hk;K$0-*Pl3P2y{SQ!%H# zm=kV+>eX6I+I)78Af8u(dK^#O*w(fZ;YbwaJe>cU3kvEn?m{TBLS**jUVByLE&(N7V z<$D&ufwHS%o(WT4wFvdik|jVEcpc$~QXV$<%b^Wi$g>G2dmi$)iZWRlghpr!bV9fQ z()<30||HAG7NwTX40VtO~wEL3{M;OccD+ES~0F#<@yJXQJTU z2HXH}uYfWW1=kwz9e}+Mb7&+9c$S%ohof~lpj)$6%#wi)OAioykMto(XX4;uGZVL7 z$m$Am@`}$z^r()*%ympMtZ_K}^tc^|!K3<=@_Ixsf@B;9f70=F?jqJvkorOJxcWi* zV(u!y)L()>EA|`M*HHFu9OH$@sE(X}40>Jdt#>bXoYx^;)du}$w#~bOeuv_lF(`(S zqb1w#jD}#;e5hJZ*$;)BfpU;)31Y5u0{y`xx9(#I>&$jF0!yGInb~T%SOaB{&RD>`53JA&b1&OAdXJ ze=Lk@NIsO1z!s8TAQ={u;ioNs&#(r@Z4lo~g?p;1e}FzF98O!pnwKbPpm~M#4OnvQ zTuZXpVbWhi+55;a>c281T}6O{=uEg;lFe2hG)Fj&r$0?05&8}?pAw-fPKnUN5lKX7 zvnx4=oMXhL@?m-T{U#c#wVR>t)Cs*EA$6zzT=a!9L`27Jtb_QMot257 zi5JKoad5cl#^+%^4e1_naD?f`zW{!R)Tuwi0#<8e4xZy?U5x^@qT$t?0ff|w)4AvX zWr&zsF^beVkXo^Vi{%il@r!MZ2hsC?w1$UiC$U^JJH@hGi@$+y=W)@1kPv&N3+7y4 z!xNg!-cw};(^PY?rSDhl4N z5y-5=f_G?;)^%9WJRa!$9L1)~Q?sCblS~dl1Rv4xlK%WzT>nESf&6qih#7ob$AfuQ zG5CbOAg-?}axEKG>GGy!i4!M$39 zoR?gzETEHX<@dAt=g596J@Idw?PR>^}E<|&+8bIy|q17_=;G_R03-63PZ z95vud(w9NH{TQ69*n6b+h@5;CGB{6-%38|NA1H&FOtu<>m+0!W2&fap^mk)3BJX$R z7IFzTR0Hz58#d@IBrLt@uSU8*q_Z-hyfc`%g$(>NF)J@aL4O!JD+m5%Jp5a-9fEXL z4jeOJ;952uA)S>2KN+wAKrVz?ON%k<9b;B^wDytWYHGG6%zDR|bq3PyAY;}$e>dxc zgjqdN(7x8J&rD%^$o3i(`<``V;By210dPXVE8~C%4A^oRa}5-0SO)lo0fzz%fv|Pp z>A;Vf6~yN9#0ug|u~k+OunJ}O^kSqZL%M-S(Ky~3aS5L^d^iV!@y7@5!ruH+7wIW@vD zn5R)Md%1xMQ|8-9zY6JAaG}FxLdKB3#*k_NzEELioi;b?QYwO?Gu-eW7@@Py zZSZe_bk^C-#YQOOM8wWIUy=F((piT`Mb0mf)mVncBqxh?O`wk(s>Dv!7`uU30jb8O zT;xL;BBt|LC8@rUYFxy{We{`C6tv0WY%F?Y&S1G6SPmvFA?f)DWfw(3@e$J?=a{ak?uP(Ahjhl>r**H6l6iz1K9R6AeI=P7wX`J{O`!}CQ%i@F8VaeU7jkg{ zgbIhR!zGrHSn0$!(qWCc=qHxlRv(ec#)zxPa3xgm0urf6#4g2^0vDJ?!5TQXsmf3$ z{O!-t_gPpfyFs3NxsZkD0(AcG)a%(|*)^xKPfov{I@l=LpFWjeJ9B%QTJ|TWvQK6Y zbcHXwCyB{>#DO8$eS3K4bxz789jP}zYiESO?K)6jrXo*4{yL>duF;cCIjd++$bWMP ztVU;qvq=vWOtvKhcPVx?>4lKa9D%zP`-1eFg6Vi(tJtSE@&zg=CPzmC0W)$N-NeEW zipdU6AZS9w*#HBe>^`*m|KsaSz^f>-x8L3Orf+U?2}>X(EFlR=SY!z?K!gAxfb1An z5fKm(7(_r87equwMMXvhmC>lU4x_j$GH!zoZnz<$qmHBKxa%O}xS)gj{od;8CS?47 zd7g7qb2;(rxSkFJu>8{X%{KZTqY8JAOU1iH!6LnBG+1M?*`?T$WZ5Q9PBgHtPn z7K4(Xo3yew(3K(Xk@59>Nr$#W`h%I2bVE`K`Mep#-H4f7tR%ib8kz4<9cng3+c0kK zXa}t;0e4yOeDQ6JnVDAdvYuMIG_#Imu*1|mUrequSLEVmGgY9$PEu|hk8eb5c`ya2 zVIh>H+`7|A$IL;J%lpb$->bRIK|!URZ7OIr@$(^bxlgVz#&0Hmt$3!w&oN(*`k43! zP<8>Pd77I%&rAUl*EmiL%C3a%|EMxLKiK*MvjpO=;OWk=gZarzIF}30ty3Wc&Mh-f zQbz$E2ATISlfnGtG~%bq$gIoM98XKDAbfCjXriE56*p4djU!tyTwn|rY{#%x%F?^H z7ycy-JPDbVvh?m{;@LfZ>1C!2%C4oqL%b1@j#_6k0o;tixS(~%Y{#IkaOs`PRg4pX zjx}(;!$=!4YkF&<^fwBwD|ULZIZ1~TGo90+KS+@!4ZYLEKM=BTg=YPM( z<}vszDlXpu^=P0>msPjQqt2E-0Vo}*R1(5%%1K>@F$4B()L}M5vTiZ2fWGE z$J46P+v^wxr}Vywbe5LBTaBc{?`AoDJLb(t*_l^46G75nbU`rVaO?CvLBCquR7N(L zdkx#KLYb?8n9o37O2B*q?jm>105%q7_;b$>AJqJcc2?#^$#cx7d6wptiG4U^*(te5J=7YYwxS2AmZg2LGe-a`?E= zu>>-F)`ZMTu3Oi52gSd1iE~u&weqU#I1v|W#6Wyh@X_WOM5jTXI(!+64N;UNU}FWUG@Sg z0AhyEwg=6?{sw~Ap~<_SC2at%`Hcf>>$!;y*owc8=nF8PLCJ5xe9dZR$2*Ahm=f37W2Gr4?o}go`)OtC3|g8)cD}+G^?UqDB)i$~ zvWkT#^5@pctGrbd>^dU=xz0wi()O-qpDOvOaMzObrBJ+<$SnjmL4nVUje>sTu-fiU7GTTx z%ywtzQ7Cyj!oz)D1xKYl5UhMYth8M|`nm={hmNjuLI_CZI;J}KhR9XOK0+2KTE0#z z^K`15S51D}VtZq#8r7uZEw!>g|BnoHT1j$^1sv`aL)C#&GIx#dss;4dL3?elG2338 z#NFKNwq5Dhoy*%Ulz>~OM%HN%XZe2yEBN(QG;3mgki^F!*-f$RQqk-KQWkt>*t~V* z?S`SuO#^D2=on|>BW)&*2=;T{QqH{Ev#LAx57jZS?gtC6^CGsS7VvSA7W~0J^Z>~=t(WG13Q`O zG=#D3648bixoHhVLA!_yINogJ3s8OhH{~{gl+!0v;FsrDLoH03fj@HCh%y5s!)2SM1@90({|PmapR4-%o~pjb}Y@|#g5rXWZrmYPn!L$;0atd0sinp_ z1x|7+Y<+3cSQAU$rB~A>%aDw~!kAY=lNZODY31|@%smF>9U#FvPg?Vc&ox@#TCL1oHm%lPS7}{<^t@0Dp0xfz{92>c@%6yNfuUBRH$YksAiXct zf+wx#i9c(!YOK~7p;oasNLn8teLvKKC#@fde`mDDS*^Q6tqxv)X*Ic(SqfxI1D>=> zi5Ej`sGymqUPJmjr?R_e9*zw`&=1Pqh^*;JCpnd6UTbZhUk7t77(TaoB>o4pDMLZ)hG$|gx=l}GIc9vZib9g@Z{8^#J52ln_;!i47K`sgO!!n zk-i2QEqK!UocO1Z)8Zkkbx){O?e&qCcN;&D(Sj$fT;e(6x7cB|J_@x)cs-@n18MhA z3!b!w5kJ~!9k5!hcH4Z-^2()kBGT!h7CdR4L429fDyAC&a%w}Z#okfUx&rCCPz#>4 z{z&|Gqm^s5E(*0y_l}mQunCAZLG6dzj?EyNxfY?u*35lj zpbv#-IOX{GXLOvAliJfg-{DAOF3x2zIQdw^z#m({{<~{lMbIAcQA)1QsVBkR#h2`) zghI+FXxJM|3)|(IFDIT1^hC%!!m51DO&59%jEiLB26ua3lsO0dr4^2SX>n=I09fn! zGM}6~WeIOV5HqVar(0B>nzJVfu68A45&^u4n#=oH^KMVx9z-=V8 zHh-k2L4l78t&cE#q}C^t=bCB7T*q5vC1XNKf#a>R))s}lE}&x>yAqfS?uf6kT9<_g*^6a3 z7`)AjZVyFmY*NXsR$?3!*C;rXeyQMbd##^BOo*yad_0k^p6j!bGo8AH)A(;Ztko?( zn=;c~XH^3El-N+++7HV323~kipFRkDqV5RfP5f_}0^}Hdeg3ng6tq};1h6ggrm^GT z#(Tnjl`?!8N*+W!)-DgoREg4b{-!iLN~cBaB6MuJ8Sv|;n+A3u`lBC|^8eu+HS$QR{HZg$RAoMv`nG;53}6=#_4 z=ah~~CoCO!`8G|; zB%Ie?34ca0d<11_^GQWsxw=HgpJ>ET*8ZvDna_iEA>L8ECZ*zy)*ZrF;-ew6m|fAz zIKQ0uV#qg{$TyjIhWP!E*9JG#3Esii2F*4t_3J8%-WzNoP4E*)?8;d_Ehl@K^F!A& zq1Hb*J=&zWeeeotoPN_^FFttv3dgHOOYObuVOsI(5ykBskMCX>;UFU%8?PQI?rfXT z>_8>#LfbJL`ob3V^n1JTtuPB-Zmc$$4?*4cn}pG9q|huUd{&ZJ#5qa(ZcE0%?u zHfGJDT8`{!s%0wN0#hxCV}y(OgO`Sf0&IB4u5vv9E9U4Iq;q7`v?fT*D%Zk0d8`Bl zt6@SU3@$EQe%ingcOjSypvQKNu98aSo1wR#mQ3Q`y-jrO!1pb4F}x17Xc4j2Cw~D4DoJl)~^PkPf<7zlES@^ zlUSOC+E||r9`+*p9cmd_L>0hzT5Nw)r+}7zMGKIXzT4yPED@eYu;cOxJXzGeVvj1* z&^nyVktjU|n{aW5rB09GX+G_lRJP>-D)Toj2|MTnjL5mNLK&fZCWewmkCuKhkQJ!7 zb#DR5C!Lo0g1m*dd!R-qIXzmN$BX|Z_9Nu$0~J{fP3V| z%-2V+f#W**Wi)qsbdA-VFAnWoi{wt}u*Pt?-`;Pt7&A#bi8-LVKjz6G3Su?q6A9{@xHccx9`QFLBBKTFWgPv#>SQai7aQI0^*=i~OkGJx{oY)q z-UZc;Qr`!lLF$#LPbffrvws~@MsS>e!K!a=p@DELs-q$Ewc+lY3v>-$LVO`K>LjQ8 zpR(Qt?_D_|`z`uhqeTmsINk3wYcJP<*Z?K>V*25r$Qmzmmc~m4w7SwMO7GHU1KrSR zu2n7BFw1>SoHj~7F{u|`UFXk0wU6;?Cx@?~VD}lw{}0`+`}L-^?<4*NG-}Dx z#ZLFSbZK~3&N7GLcfY|j*XDO~8wO=wgI(34V-_0~GO1@ciCd>)aJ|3nq;Qc>o~xOy z&(%uR7_>(~+1r#`Ra4|1 z`YK~xCg{H1=eIK57t4E~tS37OTeOW7erw={;ycrc-xcU^?|Q;!`2h zsg*$7*-TsSz9{I_Z^CgM{i;)UKPQ&?E@6!xPT^HE;7l@}H6dFojAs>l1J5Sq$+Klx zJ`L(@JeyP~&)l(rPr)mBbx5y)0-q)upI#vT9Atcw>A16rTIoI|@acRwuA^V$)0~(y ze!?+`hZ$Ggxp<%g9A?VKe6P6su=ZNVH4~BU!C90};l)|#^iIjmx|!j2gCcW{#ayfyf`@aoox^v1dyE0|k0=J{j-of3zrk!26dEX%fQ*!adSV zz4}pdJt4nWH+U6hTsV{XQfSny6PG&O`Zm|)(7SS>d4RnSOP+HkmC&ssuZKSO(vd+n z_*JXv)+^ab-yP{}xcar+8%fwa+TMueYasJz`)&evK}_?2YFK#~oVir|@h2uPMY8_W zKLMPze+1Txd^a(y6@kY7Fe;3phek3Fxp}*`TjTsC#jI9ga64p1;BJjOR)9J{{2M69 zQwy7?Io=ek=$U3WY>=#}N6t25Ey=crv4J2hHOf{{aBc=Qov{IkCXn?D-^^+n9gByq z?;sy$JX8(>4_)J7=lfXaf&vd+IFJf5iy3({Gy-#x^l&3A=6`vq{xeLOLW*5dC4{%|FS`_UH zrc!U<$heC#0o>3!ST(Knx*)sw2-kTMnQFp(z06yx`$@Mq@0}vnT8>1)N3YI^Veu2# zct}~%Nvfk6njmV#%P)-RGnE$z3 z;CP#1t5=?XIQvc5D#GjQTO(a>KLVBD10lYP^5GqTEk9pui1%L$@E?ZwO!HhI3$`hj z=2q^J*r9wU^DR4INyRB_--A}T*j(X0n-N~EE<`Wu)b^5e=sLx59;XRxgG_(zy117X z)jlBp4=89wOHI%4AEL~lY=(N%ik6wP~g&2^3WZ;`6(ClFw=qW zC;mt1V)vR8U=BCTq^}1VU1yrek4V3Ryv-O^4;+M9wE)ut(_(f1WPmrF(0j-tPCg8g zYO?&-c0yD+$u~h);Wj+1qyuH@9Y%9ZPCvJPUkhlbP(#$&9Wz zU1B9F^R^GuRUuR6U1HrTKn*0`7c!p_>Jl5MlUn$Mln#dJ5;rxl1nEMEF5D&fddk(r z){8UG=@NW9;U4EA$RijG9%Yq#-<~x0Ks9_dhgxum*t7n@px^ew(OquAU^CNt&YG2JJTdu!h zdN|=*`2|(7iKs3yyTb&WN>4Uo)m6o`G^9%Y$55JXr5Vl@lzLjz|NQvt*^D9ktyokUy{G=M71yI;mBsXvFY~5qAtTbE#S?DfSwkx2SAS^ z*ZCm=!~+Z3dLs*;SU_XaqvYJ)mj8?8ml?jcJ2`=(j%meZuQ^$f<*^L`CTRdg7O=P> zKqpekLYW1uZwN3x0O)T44>bg6gBuo-6D+_KXSio-K#&6`v8gWwd#eBVqkMv(Tk!19Iw<0yc=0JO90`Uqfa*>sa59q$VZc)THiHKTDZ zfNw3}9}NM0g0k>_v4CG10{U7&IywB3T(tXFm<#JeXrY+}R5t{K7TN$%TRpKMAhb|w z0p~XagciyGs3dP~2rwl{d*}l|1+}9gpj^3N3*KM=3$gI=ZvwEuYb_=TpO$b~qY0bb zBmhsp!NY|JkRA+KC{IQ9k8hbO1u_!MoB^wn%Sq-e0_|v1o*GpNjbw@i(Z$Y$rB=5% zWT~Iw?JCQ^)bh!N;a>#*aqu^Uc*k1{>lRq|Sr*#Y!TKGe;?96&CxoZz89I`I9@n_S z7ENf;zcW|wsWst#?gH#oYTu+-p81Wd7tG5v`zG7c`dh09Xy4?K1bRS-&mY6j$^M3k zp`DYklQljeuGyh{47ia{#*IMK5`qG@?Wl@%)mys(KG9!+Vy;mljkq(-eY;sV#uQOp5D^)hRFZ5R=fF&v?5UKE2k#KX=+&UF^)*xn- ziHbt)ie8Fei{u+;-u|y|TsuoY>;<@(HGk?Z_rs$VF%KjIM~h2?8-mM+AjtFAoI{~{Oxz!P0uw{8m3Uw%W9GtrY`Y3S;Ah+c{!p8;*D!Mx#3F}p z;HVjmHxtkp@+l^Rf^ocR^{a3+e!v3Kzi9vnN8`rS5fEmx#(s$A{J6jNm&V{9}P!qE3 zxV^~or&xYCo}UhX5%^Q1biCEDGAiV@H7ed+32ZMCGV358J)&?lE3{B}UNN>x=7K3yZQ8P%lt2aou3kT^b zGq@4E(&v1_^a8Zpx+{Tu?-ErxEFlt%MTd9uj4v$UFkv(X-`fnM)BNYRIqAEM(H1{4 z2Cw303Z0uVm|u;-`Kx35NFn|QB+ugh4k&(ky-3Xr;msHUWNrc(GU=h zhs^=VhxZx+!m%jd0)A}>2*;w105l#J90<#y!FX710RtNXLJL&@l#7`S0ih2^TfhYk z0ilIa0Qylw*8~7=&oXOa1p^X+U_c^p2cX9ykT(~W+QsW3D;$t4|4Yk{1|<0H!T)0U zOb6D$(tt!WIW!nKm$+#YX<)~h9_yA)o=$`bn0z!{Hovma%+nGhX^8n1{uR;SAa~3V zm0wY!!Qs6}cr6RcsQazKfg!3i(H;}@ZRO4M?fl|4GH#|r`NbWB>ClhNlk)TYE9Qk0 zuy3M-@;V6;+s5}7NGN+#(6{r0zTFviF_ifbn$jV~0l@R=+qpm4zMY?T#Gw?^`UNST zz-W~Aj=v`A+ouBeHrTFv43WdSZ#ciY<+u8FIAKZX`*LZQ`vs?l^Q)hJrf;uz*_{I` z_*LIddnWb@Da5xRnS}cjp!lYGk>t1Fm-RH0ZEjB`GX1+0w&~wx1k^JQG;}oVe0>1u zo+tcCwj0GIu5MYu3FxdiDGz6H-cu&WhX#qDh1enqU zFy8`rq0bh#b<(UQ065(O-fsvn69E9L04STz!O()8N|+&ptNNt?)O`yZ0>ZVH4Hi(- z5MUP^yc;dxq=o?F14(QKppsl20qhuKe5iE12Q1+B2$1rs)tOCE19%*Oa`EhM0!ZRn z>%*rJz!p@Pi(NK}bQ;`Y4cClPRMtBd(5WH7j8XtTvObLXO#oxoX8_DlrMCQMTIB2i zspY1Fu++>~hAcZ&#TNwpYb-w;sxshT$8_jVmd`zR7A!MEYD3^GY_jB1E5UvQF*aUj&tPi=@W{rM=CQY>I~eSqWfT_vM;d&G9U zYPAr1_g1vj0=$(3l+#xuq|IqKWmsbY`x^qxh`~+RRTj|HF?Dk2eNHoK0jRTp@`eC2 zY5}+dfO0v$A;5HJa=F!7V5Q$yNB!(zVY>xf-4I}g6aYIdU~2@ZSD{)%+G7EKYX~q) zqyXLnASVwr1ek2H`uLdzX+54AYXPyjKk4z54_l9$ zP7c^=IvGtYR0#!7+nmyPiiX_PcQq`SSw&jc4rW$SI-@6&w!W(|#7qg&+8&w`q_wqE zf)xxsKe48PFhRz9Hh%HpmiI=gOIdbvqUoh zL|-VGL4IX|cE_^_{z`9Vr6!Guf)>@xO+Z?`i9%X9QE1K0O~-V9;|aX%X#99G&MJa- z%%(Hv34V1=@7N-?4nt~s=5L5XGovAbs7;Y_+$UK!{buyCg zw(?`FJb0R=IYW@hdsef{}Y5|7})tJJgshG2cJN8?%tVk(>GsgA6b8ab3!2lbtcuGr?^zhDbF1G$;90 zaq?gZ7b7dV{E&q2LjH=Rzm?n;NYYMo(g(D1ykVC8q*XYJx6zA}zo0PAO5Qpo`K~zm zGw(M|wG!p`EGPZkR+QHB<8ZTuUptHA6-*}OUuTn{7VVTt-c$2tAl0ze(GQwNY~zm% zYniiPf0E9uV<`bOJT{6eUuh{iTeSv_^i`ZYjBpl4=LEvUm8WiS(|;uk>#Sf^C|F-_Z~`TqovHvSwDg6d_i=pe$DNv0x&-5HrWG&RrNf0-IC3@P7PYiCGe zUO+3N734!s;Q^IyWe<;vFf+zmWNLbp*Ukdb01J$h342@U7 zavl9@Xe{j(+lvu1qRvBeE|k0mVK}0)_++-by<^lW=`f{(V<{|{?QXG*kKo(AaSR(y z=5@qX$C*+vIZf;wjS_D}vi)zl@kMff&@i|eroFEr(Up)tf$KnNaNfyV#CJhnIwhvI zv=k+kdF&#!C9fH5HKD64JGH3N%y-Q5>&zUarll1SP-Xo2w_sjpq^i$%8%WK*m2tX+ z6_o=CveBgg=hFOHXZVlP*SO-7LYoXXDE*A(sUCbS>#h^kQV^ zLE5inSv!e74>>IkqLWS_Mrw_wPe0Qs>1jUK^G~GjLw;-o3H3DJ?a6+U*Zm;B30=$b zdJ{hq;%#tT9UfUZsJYyx`y-fTCh*zkwG94?;*fZ*sOfn~j zJ9!1Tcza~~xR$d0Gx+ah$Jjp3Y?&VL7p7rQU_0QQM0`5rBsU+nIvq9(Dn5SYEVKJN z@hZ))q?KPrp|nB6t^1Tjiv|-_!*vpKSr#g3;g`{Xi;Y~XYi1?9q=lJWUP3|_Lcz(i zB{}9jG;{JSVO54XdDez8_)rztGkk5P=RT4IU&CP81VstZ#B}p+_DoKJr*~gY5BRLJ zIc2lqU0NT<4emU6|Ayx+C7@B`&H#`W+xC;LKzyCUyiAa}%E~j=5_q6dkQ!4oJw)YYj62Bnoxw4sAN>l;1b{&Cyv>A-o9PgxWG|q3pirFV!JjP6v@1Z2_gt7}5H2rzB z{^H|IGu{pJCCIx6UEPU|H0hh|2#;IVw1}1YEG*sAX708D@1O-;U_pErGy~sciuzOz zobj8dkY1l)^-=jVJE@ENnk|*Q{>o5Y+}3}3N;p+f0$CIKh-;pUG~P}zKv{pAE?##n z=#3>l9Lj!+KJ9O7jG&$AgO|g+4DuWt(`rIna|>=ZHfLs8Ub4sraM3%<;jeJ&71E_g?*(+8 zfs!+TXxuYHR%L;PGwnL%Xlq2SeqsLB;AZ z>3^+e+funjwLv8q4~_BQ1IB4hFSzmG*-pmb`l+V^KrCZ>W9!7P^!QoR>EN+kQ&+9* zVu@RK>g7dnoBIo~ZXV^tniuPNyff?0UI?0)<&T_0@X}hiDgQ>nwL>75YGYPmOYdjq z5aU3$ZIL9sD3E-@Qfe&jySpXP=7CteNJZyN+CX*3T)zqC73ZbMOQuoqZG4C9FifbR zSWq!DgA7=%vz<0km>(;JYpzYX8O`B`*Cwv@N#U9`wME5MiF#nd$9i+($x-Mw^JgCy zwnpv#Hnz9Ha-B7@ofo?xwB0M?Y53ue{B(*Vb3P1?#bsX`0oC^XfK8=zY+|p;kEBuN z!5!8X3wk32r2>$-0|CRSNXe}hgOOmvb09RQPF>=5r+XDeVY%;LE zS->XyRT1n2!K4+zjs-JPcCKL3ieL<^i9PLs4fj8c zV0Q>6tq3+0*t6Y%4fEfPU@r?Mtq4Z@P8?7pAL_pu!M+ttS`m!PMxv4_VA;|Bt_apT z2bi=X*kE8A)GUYiJ0n<)VA6_UgMgKA@huzd?}%W(6HHnWtOnRh!3OzHMzAXclU4*{ zlu3L<+bpZ``K+UDmX8P~tq4{P>}|D>f&K##>^;Gx6~UMzBtGp6tlHla!O~g+lU4-7 ztwf;`9pK*)!MX@0tq6vp#O@fd{{E&2HeN7kMKESz39dd)SwH`}2)0}>X+^N3fc^ah zU{(Hx2zIkz(u!crjuKtDWS8~z*F~`B1(Q|;>kTYE6<8mCO$7T|Flj|FuC<9j6aYKQ zKR<%y{+S%OI`f-zZ5>|%r{>*X(s zV3!Fdtq4{Q?E4PDdiwJs*aLz|D}r%9nV9DStMF$_~rF1atC$ zNh^ZUsS;a%39~Hrt2_~%w z#@%w_l;*&?`$tExI>DqB!ALJLQH{KtUmd~zDwwn)7i3CYUkE0x2-XSM ze{`+y;#WklrmcWUD}pieNnE6%<_N!A1gjKGS`mzTN<68drnBEMf=w4pS`n-i*yP#3 zI{8Hr>>|OW6~U;0#D#glI{IxQ*xiCjD}pg*CH7FUWgYyS2)0KsX+T`2K0B3MytVA6_UTq6@}`6(;*;}LAQVA6_UbgD$=7+~#vzFKPs zu7!e0%fehPk%@h3AVvOx2z9kkQVLO+NpxmhEi3fDiBOLTC8ZFhi|GlfsRIA22=#$b zQVLPJmhNFlDr@I|5}^`pKuIY?=~B87gNN+C)Yz5}$}vR3}{5$YMCq!glXDe<04 zBF}$1LVYQeltPp)dnE}_x&Gr3s%cwLQVLPJ>Lpu&YU%S?Vq1^Bg_2T;qH+?mHM->Z z_eH1^gpyK-(lsxg32Ipj|IP??flyKkQ3FA>O9R#1zb!)jNhm3WC|&Ws({&)*zcE6+ zDwLE$lrDJhYc|}>-x#636G}=UN>iOxtwA;QuZmEu^Fc`|L}{jTt@=k3e{F;sD3p{! zl&+lvnRAvk_AiQ1bA*yoh|;CAH;un6%U>0tE*DBlAxc-yXH{N}{N)kqL7}7+qIBVW zA_r8;UmBs_7D`GXimO-RWT7(s1rh33p`;X|blJRsqAlY9@(5MZ4wRHal&+d9nar0Z z{h1MJq)<`{QMzcJsLNcsKQ%&~CX|#yl&+cIZ~-Vw_>&^kwL(cLMCp<_MRR@MuZ>Vo z2qmQur7LEOW}wpi5fSR2LP;q^>4G_gLMw~=gCmq~wlky@qIA9di7rxboZp;#&y=Iv zUY1)JTZu+%!_}2uweD0Yvq_KB91AM^!IQ&nnv)(9r;FbP4%ZQ_JKQQxuK(7t;fr){ zVbc4SsFu6d6ES!BtWy}XX1C9gPUZms9~Of2Fr}xIj|ND4FKjkNR5DOoX<#EB*f@%t zXdR)MXqHXdP5o`yDlp!3mBk%IWw9}lK+ma{c5f$Rw*?#~Gw8cMzB^)+v%wU|SM%-XUwo1OYN$@x}#NMbg3&_@g3%wPd=qFM0D0L5hi zll|MX@VAl~kUz2H-F&``^b{x0LHUa~+ryt|UbUW0d?sYJ@c0ue=?uac|V`9Di~P4xZrowK76;-jGKN+Rse@W+_1pDcy70808e zq19QMcj63ZnN!k(%M=H9?W3?0H*#U~hx*&`f+*FY*PH?`hIW(}w_#!v6nHVTmsw*a z{)~9zjrI^NEAAuy1(cm@y%=V^X!=(kpFqiXNx^zy&Omp@zeN}K2brUa-QSUFu4x2= zIx5`XRQ{m8Gz+;pq7jeO@|oWFeSBA)Fu?XHR)Yz!eR>OPcEk96hAZ4*>8XnxKRA=g zDb*M7{4(Fwj^#kRtivJgGIM}tpj|fVkaoFwP{{+!8njD&S2+l@OFJFXF0C?YXB*HM zg`Lvjj3j<*-&N}gG+Nj5wsqjGDZkbOgJs!I8T$NYzI%D3(X8lD8qLgMoq>nVh92^; zdEjB9IfHu96#p#--Czj)2WGM!;$mDhrL_|1L@ks+d7CJS*AdcGicT=ElaGNt0&N{?Og?Km^%I}C3ZvIJ256iRn#{+mD1OEx=w~)zB1xSv| z;a85gsY;lW#O4{UehqhMA~}h$vV&i->i)WDBt~#6(Wb)Rh-<1(w@w!ub9hOcimvA} zj&;4R*H{`!h-)$3|(>9*?Xz|vGmv0VFIG^|m$Q-GpWIbmM zh1N<#0*Y1VPNtWjn-kL&wtB$@7KVuRA^oj`DF<;PMfE|E5hsL9}J~wDUX|dPU z);%A#Mv@f)TRBXoEYx%fa|nr($eF_!kn|sqe9|X_OzYhNidC>G1&P~qeVTO+{$J;qT*(vHyKvBlY5k5Z6*2; zlsy-3=*zi%T4?Zp8P*Gs;}pKgbul-`9Ax{;*sz)Q@51&h9b}uE(_H7+y0!T_jyE@_ zRBjzW^DD?4g^`<6DT~KGM~{OVEpT#MnzQupBX*}avnrk3+>{Ok`IzY2Vl9D{XB-^x zJVOMOT`LD?9f{{{8tXvZKf$~iYP3YWw&rBsFNwbo;r2s~a4u=w-iO;kE7HUMVLCl7 zH)-7-hSN#&*=BcaKCUIrY2<@m;KUuM(Gn*&!+18I*eoc!%$8H8DW}_D-2%}L>zLN( z#{9QPEQh#Trw>UDI}x5a(9W%8vX~ohF@zSW-6=P@7f%Ltd7|j6f#LL5drTM1;72Ua z{KjSK_lcNc{{ow#pI^h?kcc^#y6s8Veb~>5=~&mkRgCh)%5~vFxV}{C2xT=8{@(9l zgqOKbBlPZzwT@lpcEK0#c7ki&we%J148g?)(PPr%ZLL$*Pw7l?d7;2$c#ob}i8GW`gfv%1FJK;H(d zd?!a!K~ByxVh;gGSBcc=F>U-JSjQq9Zve{w+A-nPO*_rZ0ir7qo@oFo-rE4s(6F#c z03DQjGr}7nQ^eblyN-TM5kKmuEjR_tR1i;B4}-7$9uteu#8 z8cIDtB=sV5ekk=Mk;Me&L6y?{5Y6CMO_NUw8s#mBH$y#*vXovW_A)emr&SU>^&*i^ z;C=}4m!pq9AHZTh5x4FJ`BDa}_e<;~f>KpPrV^N}$S@-73H%I>M7+3 zQliZ}Id9y`i=9AwnWVCNkd%(je+;BP2vPMd5qIoD_cp#iG+E#`jdlI%3fX9hoAN@X zA5i!Xic6`{s}v2?Q>spK4+^uDDA(Q7VC3|?h1aEEGL}=#t7YS8SQV%gK&f#=o+9vP zMWzwyu#1B)p-O4CsZ}-WHw*o&6Pg(1-y^;Z>M3QT^Z>E@AQHTv9ySSuX(qvMl;B@s zy^8c1D7A|Sr&Ku{VCAI#L1Zq0nb75~RF|6+^_zo%#u;voQNIbm4bTLsQ~N!oNovzB zr;7vbXi__^oYWS=dK%~hP}SK)DA$YmcW$M?j~e^>)s(i`oRqbd8fP4s5N`AC4s9z6Mi{o>; z3WDL2@rb@m@rVnauE+9w%NyAX(9Ck8?A=51ymtog+YKaAH;4wL@ zS6%PLrMu+gCL+5D?1D0;0t^;ibR1Q>sS=kZU1Mbi!k{BJ?S&O7ev8rSMP?UKXnE{zoRGuJkFEslXfH%vcJvSvIEQa-kU7%oOak+u*|P!uM;7fNRvC+b!=jF3xD7sQEcA=TvZ#|;biDkV zkU6~N0p!#Pt%PG5=7x};5&sv;*m{`Rpz8!DmX)z4@F-HX8wNlYueK8l`cb1wk~#pZ z<=b4xp?DnBZ~{XVX+mT%fq78Sb>#v1vVMamCK; z1IS~f&|WE+<|ylGj_qZcz&I!KAfTXKYE>@I8s7|43a`kuM`3Lx@h$S^St4ogkY7c1 z6B$Ef1eAJ@$Qc9{LP4*>484k9)2n*KylTSg=9gm@Uo)3+SbY2R^5UgVk?ro@2p}q? zEdeB0l;&1ZovYlLHVtumIb?DoXG~7yh{=hQqjlU_poW-UYly`E zju47(Akv9I36$DIWE_DJkSS=H!Sxsg-8bf4K}^mM#$;H~?AkM<)U?~=m(nqQDGlS7 z>|4LIAiD)rbK}|)<$E@(dOg|HsA)Cs_x@D zsoPdFr zQxtlxLF;N#JS0Ht)b}MJ`W6Cp2A!kRIaYuMimxY-YSAj9^2u4Q^AVeH*kLfvE!WAE z>xc#ypPY)lOhjgei&uM5jSqqKE{69grDKS+VaXr|N*zz6mVl04H}xaUn6rj-yN`_3 z_`IOfUfQM#q^T-E_lNg*%7C!Ik$>&_a_ba-pJ1(NrIL6?u2$Op!q~LiWJn0@Q+IOo z&T$hKJ!*~mtEk5>g0&tmE`~Zuo{yXf8ow$8-szkZ!af7?3FLSkk!lcMiAe3od`8uo z*~_x8i`b4=P5eXH=d zBP<8lK^%!!5NS`O5c0|a%0M9&V8gMzPx;@yetCGZkd z^kJy$@cOEgql3f-6AIG(TDmJ?E!)dWw^002BHt1C04kaW(4@P<=&F3S6RL11x4Q2A zZ24UD0j(7>J3dGAHZzW<4a_dPvY6_}z^N5WHQfn;Wp@x9)<>spZXsCXpAs~D;xy}N< zZwN5Y6OVF=HUm)gdH310TI1$n&Tw|bqgGbcP!_&RzQZPSLPLOk6Fm8f1srDCgQ~l|nyNbo)_UsX_o_O{a~7)V%BVWdXzCS0wjzE=Ro4r#s&34H2Bj5NU9|-) zYzPReZUg{T-4zW1Vbx8rfcqN)!m67AK)uJCYtwqOA(E49fu|Ud!U8vN4!r}w-2$ZE{Wu@JcU(vzydlq z1cX(`1;JF^$cBKh>QWXkzab#3x?BJnyu1Pea^}j0NUE;0l|9f<7FJz_P3HB6fUxTN zTfkx2jH+%s`IHk$N)bd>oesI5 z5C2RvVEzm`p7@BFF6c<|5c+cHFl=ud zGWoeTl&v+7CAY(tpBX!?yuNd9F@5^>G+s?I&Q%U#a_})Mx7N%sB=MXpaH~f=Hf_pc zCI*rQ6NB%VPCl@NS00V(8>q%Tguln(OQ2MW$m0aIDUwU%Jp%7Q#M zcn=)Si*Cfy1kFhKwgN}F!`1hN9)OGEDrN68cyjz%%ftC;&QhFH`hjzqZ?RB$x|2TH zIQNesIF}2n83iAQ%)7+S8JeK$7cZQO+O#f%foZ9BF_1-2(nlD+OFYP`iRU@`HELti zP72l5pmLGaQuWjZTQ%`KC-^0`acP%@YB!=%2RSA3x*VLNuTSYqWWz$oA42{BWIn{! z-8WJ=oKLo=@+ehDhdv8e~Sls(T_HSSJM%a%>$@?*gNgp!q3LFKFMER|Zp7Sl7o zM(?;ZqjM1aPtuVOwq(5YkvMOPv<+O>(XUaOkhUsJEc;`cJd`Y|uQL zj6C=?iJzEOaWpV}A8!Ycr=jFDAmQ6!Y6nsi&ojS9ZD!gjq1rpByd||+_0$GiHSs(r z_{HUu($XjLsPFqogArqIcO$JCM>+ zxWTVcIyr4knApiEog~HO^%d0)q$j3levRJjwC$nZ3UroBZ&iK0YD!3YVtVG+w1YW; zlk34>3DFK7rbIUbVkw_l?}x+4`m?aMAl(ct+D*hwy~ppHM0Y|ZQpq_}m7uq5_}Jj0 zP;1bqv@a-If|OKGB6cFwQx-D2prwI0S#ASn#2xfs*>b#XxSpA*n>(UirCs;k4%s&U~ zTpT_Fiti!vD1og|>H{MGAnF`O-MywVx1eB z{s!g!P*<7mDU(ieEpUCQHOjMHGc3bHeK39LIiZL5W9EEU+fT&voJ}NCd2=i_XB$x` zX9VDv2o+$}t@{_?Dm`yTNK{pY9rgm3&QuPpG9#(1uS;zb&}{CM8R(k z4)ks5Zv@d(h_d)c=JN#fv^SF_`E-5b zH1jJ0`XGXn(?|x<4Q}gxH3*jWayQ~XK^c<}Yd1j9 zZty29_RK%&$2u{mfDBbx`6x6GVN!d~GGDn6CV4av?Ld&&ytjGinUdZJf zfB8C`(jNrtRnmSDO4SnimcV{!vcSs$GaB%V%T88rH|H-Xb(PWtf%QNyb1vdpx2gz6 z@Vl274GR15QM8L=VX^!p$!AFD%2?`O=Eo(-{hVhh5UTrbVVUNla2j-%RR0Uht*uJP z!r1a`{#5~xBV!S#a>YQo7nEvGN@>=bOz5`&{Q)og2LE5gKS0J(!B;H9K}LBHfq@VSpLQmmtP7XScPQZtU>%S2 zSSY@Z$SMLWpw#t5ZY6NDBAbakN?;?x~i!-drDdMCXU zgKk~PF~FaLbsf;F1m8o%yK-q}5t#j}0Og$idrJSta9#X_S~M<(^>@&(L8`arc43HHZqDgBH9RLi`i4!J*73|PJYBn-9=zG0Da{k^$3v` zU!x6`sML?fpzF+#!O5)|J%h3yfVdxIYT;M{>S9H{ssb9L1eTBV~d}1 zoC54b1D81zN#E!3rXrp?gU|n1@i~AP8s~pvhsODA76&r!6v{Fh_Lj*q<<|XS#IPG%pq_xM6NDp-9c$`-=7b_}tRka2Alft3({$<W5UwP%0F$tF}GdZCFX!OrGl*<(0eEIEdrNS|HjZBvR1<_mJ^NHLq@eUaGBDxDo zeu(72oI}Z#H%8gN(=nf--6ePv7=H))wz2ja%9V)6U~K^nF7+mi{fPEK*$ZUq1ahm-=eC2J z2bpg0681^HC0ZA@9tp%WjVXvHL#AmgCa?g?HYknbw-DO|nWpgwfrlZg>K%;L&+X_h zNv;6ftCjYCBCi2_S(*olwD_L!3v$YnTexK39ZWB76*ddTv4|#0Ji0ZpO3XYbQ2~+D zVK0R;Z$U|o!zu3y`$J-#iF_hlHyD?JxkM(4Y7wd2liLYsA60wpdeU+Vyj$@5a9+;V zJGke$$4P(5)Xh_qD44&(+KjOqp@EXuAm;*uV~t*FWW?Vf`T`pAD&n~+)LjI+{=nP> zO1(>DEP;_w`G`MKtXSxU2V1Dv)JPbskSvFK9!n&73*y6?{^uCTxHp(Wel4ASFt&n! z2ul4#ZGgGV z=F?_C-l8OdljrvxLE@Yl>Bt$G#2vItfY|<{{hjJ(2yGuKd;Q*MBr^WuS4Z` z5m|#$bBtb%$<%!?k_Q=!A*cKaVtiTA+;8lV_-Po&AQ}!OpGI<6(Y%WETbMcMehIwi zE?LWXs6o-}0TQ;FDQn2p!)zHc9sIZ{ZEyTaa5as5)CPVkbn2c}|lzM{5LIS5i$*ipn3S%cyE{W;g&}~G%Qm?|e73dAp z{X3D53A``eN?`RHPx)s^=fTt{dX#iOhcWb5T!m8K5t&9{3gmd7;h);qXmGZ@6wN|6 zEaB9J)FJVdDdDq@r-buhtwDJOG*I##$Qh?7f);Zd8S(pw-hhUbAl|65DJ9Us;edLm zyqw6JD5>4NEC+kTn21D&Q9Gk&61xwj?(T?y zQo~{V1JU1wok-*x0{fw41+c>!+bX1I!fb{Mr%Lxc7>!)kC7{&hM2;ZP2})iHtbX|x z<-iEbXU81#%@VsF@UF_|4OqvcI39|>PvmL>zlTy^3QXWh$UH!J9!s9{JbA4iAFD}i zWBFg&T+!M~k)BP9T1_Yy(PScS-PoyEy&Bec82K8C-$bOuqiCVjT|`C^I9icMh@3^> z49IMhXoH!UqhH-uSI>^s%vG8)r!-~BZn&64a1pXfljtTTItSJjSXwL7rxAIW!2OWP zioh{vud;GktY(wUO2I5WIXN?@v2yby8%m71^H0FsLRjyjy+`KGCXyRtlLju{7(sy*Ok;1M zSRcWnByqC5wd?1It+ih7FXkd6c8Qy|mjuj;bRy2qEGQ#|JY1_EMpB<)=FYbXd_~|! zmPx#C2&kaaFi9?XvYwPttsdHIu9Nv8n8(p_aw=w$zwNx_ZMt!z>7+LhRy)#a4GkGh zWS2^E41pRr)lg~*kz)x=geJ@;;$1n}RJKfc&VCdI9-UUc7|9wq&y&&)+?lW>fO?fr zIqBEV;w~}pk#9RY$!d6N(WYh&HggKJf`h;@dt&z@bshbBum&zq^JWuPC);Oi7^t@Z z-bCzr{BW z`Yim?*TT~kYr&X06w7mFv|;~uaV+x+V)}&moI40O>s-AObSG3xSZ_5w1<{<{rcM)H z8;@nR&ML{uPE)a6dOn%Sy|N}3=Po(lb(^|9vfXTYeuWdu>XX?w^Qg?>*#q)CJ#*sgJjyL^pRpUTCF5UQ@T3U!8kxO`hzhTLWpbF9XRib*9%r z@amep9w0M`IdY5kU2({RMkJUXI%pHjN~`CUhw`)w0kTp52|qa_f7-#9v0F(u!=$>nPi;bLzRw z8eb^}xaV!`WKu3PPwX_zB}bK*Lapy~K{xkMK@UINslvmnp@Fn0C|+CG?bRN7WSjO- zoJl7uX-gi*x!Y>eCbpcMd0ghPnaW1#;U)_VvqSwFVMVBZ*>d+48B&QIGS#_$NvW9~ zVT;JLJhhB2L5Xt{rv^xaw?$_1&rOr9FB1(~YFW^LB&8l%pg*c_Y?qQ{JyctbskW+07H95=tf5g2Fblg>T zC-@{8V~m>+Lc3{)CB|(WcQ+7AHpWe8#`?4bl3c1P$%YULeUvO2sZ>;z{1wveIK&|y z!UFLShxAYk!w`mLSjNi|56iF&%d#vXEW@xY!*Dn(%MuRb0N3YV1u`sw@sv|3*yDQ8#fq(;skH-$-&bP~)-lF8-YE}fw(D(;hE#fQEGvG%KH41epF zo6dwNyuHbbI_QvhSmC8B{=v5?dG2hY^hZ-CQtMBe;)#$<WLFH45kcJm~>xt%hO#6N|0gfD!YLlO~r4 z(Ez-sX&p4kdaCvRuqmzP-3wnEwSFqW=#OaJAV8&*|JVL|*nr?FgDPLxKHQoR?qoYQ zXn%^ihj5=kH?e=^(bB}|Z;Z{2RwjO;j z{f)VDu~eR(nX9>NgTt%abF14f-nhw4m+;k$=?TOr7Au8H@y5dV+TT{hoY*m2cx zrqGuiD)gd~GsrC8H_%2#$!*VXEA-^lT`5+Jl|97?#|jo^iY12IQb1hL){P8g37`nb zuRMVJOwLs%3cCLbZvfOx`a=o^tE-i6}m^_RPd<;6as~-OpneKCW_T@jW<3wi>4|j ziA#I6vb|d4_~TQR{Z;f&p|a11bzKAPt%Y2zQXHL41;uwP3iv7$TNY#W(!$i-+znOj zua4|h(qX=b*;RYdlxVK;;*1-cMkO&KJFd#KX5I8IbRQd38WU-_+>XBPtm`U{&F>nR zgXdg@B`mpIrY|)D3LSHEGp>7QzEAQALn}Aj(ViVHWZJXs1BL$WgWLM@!(DxY?fs5j zv8y~iS3s8)E2Ys|(ao1J)QW}CiHS;~Ha%O!*v8!0HCpu@UqgS*Pfnsm=JpgTlc-3c zd_&FUrDM7((_r>b&ra7cX3zM-UNOED)kD#P4#+Xf2lxlS!+p}%jSFP|&qGTXCx zSDV{EhdCgFo1+Fra=vEUx(D0Bgv9u()W(XVwZhCCMv0$~)k%s z)n4ex4;HfBx#59sXYI+6)7~Ybr{vh`qvMUykMTiL*XajSY1vbt& zZ!mEtD|54jvGIwx70Wm3fEk+^Md>-*=c{9Af>Epod#7vTQ*LViJXV?Mw4~YA+1azr zVI8Pp66|vwUHPor(Vp+@?b^PraLI-Z5d97^)R)j^EM;U^ZDjaxE`~PQmg(D$K}flo z>lkiz>ECT`e!5g^-CUqxo5O5$L&NP|nBqFuqh9esq&?Ho)^iOfSjF7}ePM>HoVZl4IevPQ?a}(253g5zP5rRPa#fjWt zi*oJLmB%G(kP5%q;_Nu2M}H)wrPHc1>)NtzIM<%dZ`LZZ|2e6<`noguojqZ4B5T-PwY(dtHanxoE!zQW~1|=)ziu-DqJji|FGRPIX2L3Lz7=$E$nEc&V z$mP4cvwfJKD%otBOqS;{zf3)uovzM~a%k?FouP8UX$rYi6*;@DtEaDLJ4ff(3@7Al zbr)qO=N7uLv%8;JqqEuZ6g6c?z0gKj^k%WJhzQLMWU{S}|F=nJY|L6Yxk3bKibfcp zpP}#w%P@MSFgH0VOV-qAbxPKXc+yc>vX){ahJhB=6AbdcPONDroKfbPxiNHCvdT`) z%~xDleWl2|W(!#J$FVq7$7fBbY|nM}hSC-w)u@eCY9ftfmDI^FHdh@-NilR}N<R8=pw}_O8X{AeQ50>$ zTHT&2Y?S{uqhn<>wQby@>W7NY0;JDWp*%k`N0(x$owYuj#jWrjaFv%yIgyp=pQ9j4yy>lp`gn=WHI~ET6lrFqp~|`?%`ZVfr@9} zV0USvJO@!^qI|qonQ_{P)W*szTh+I|=GESxFLY$vuj+PK6}RhT9@x;Hb*1TQ6(cyw zrM!1mW<(Wj3$fD=<=cy2YIMHRs4`~GsA!dL?-W#tAh>0PD$LGJ6s4svQC+%yX0$S^ z*=G0Uq%&M0SHw0Ub3=^^$o#lz4`Bc}&IO1v77qfz&$Y?XY18AODo#Vb6l#^xQWaCO zN|mfqo^`{JSU=F*RK#!rq=4z|$z|P4agx#rqeB&lp6%H#O#AJfPylVmK*ct!nqAww zGZ5}vJFxf_rsgMPPD9I{FIF9dd}}s`)n|N$Y0hJ0i~6-t@VLRgT&{g^fr!z1LWW4E zwiWhP(4f9Q7mNzbFcl7}F^XQ55(m|>`L+V~MK$b-3bP~93!AeTrepKdGc^csNPLPs zjKfA*2a#Qb&J#)Ge0Q$Dy^zU=mO*b|`7eymPPox3)^gD^bqLH*oKBz_DDs0Ka%8+lSOjyg_b`#LhuFq4v}YPMl>JJW)4e+ixvp&kE;rIisVdT~ zy}uuo$qeWHyw9}fa{1ou_Tg>4ZW|_aXTIObs6oc8gsYBD7Hj*1!fG2a*;1iJWt#-G z$FH+0+8hTdQ^Eqw?bqIEEJ;)*rT;I@Vi$J+3WwEA^lMB4Xgb*P&DBIeMzRU@==3`g z$Mv|zySvbhs3K;B7vRLj!^+bcs*RHt26E}xVA zi!6+`ySDwe4G>*^$91zB)FkdR#9CllQiGz^pOMTX#mO{VzxmsK_4a&w7~k9aGXq?@ zQ8U@EMDaui%XM~ZEhvpefQ4$SycYQfiG&Y7@7U2LYJSO8docBim8@4e92Fy-=+wugWYK={_TGz}T=U zi24Om#}7$HK-W@ns_b`Ji`c1KXzSq2NEh44$_zOvdo7H;hHWYKZd^kkadZa+0ehf` zx7)hK;~KN80-HfscY9}V-(cRfpUgHtld|0--Pv4sSD`E02Uh`&I5gbtMz;4~mD|w{ z(OAd~$r@|QobEA1eqvO&VQs-A<-W_Njo){cihG^Nl-juH3|y0FWI+W~FaAN6a=(qX zDTY&niUbv%d%lWQpiKpJp)feo*IDSAu5!PHc9983%L-bO@)63%u3e%i^Ov8_d9Emx zeb^maE4OZOP~{49W7yq7_vV!894!q%xvXJl3ccipNI(^!0Z+*6?xYfzh(525(2(Xg zfLvc?1r^y}5gQv@NDM&ShGAkMK8EM?t|{0WINoI0)js2aLyJhX?cMJM1nx3Ol&oWk$zuz^c@l?aZL!#ofApwaT|`Sj0*b z23TSQWDBZZrfT&fL&4T~tA_5s*b0mc?G^ePO#rTAdl%LtuMz0v)(HdU9YT0KQkq13 z%gmI`3I2@WPV`U4Q|zIjkZg(PMA9L$7L{bX4O-_g|(-rQAw5cNNobaj) zrljr)^ItG6x`~-_H4G*@YPBCsVk0w!b(8&DhGTSwHX+#KXJ&X(AWjN(k>t}QG)H>F z9_v9Vb2wz+#{P_)SJ;+GEgm{gu++nd*?$!_o}JqV2HFR^uBLodT z5;5HSs${D=j`KdXOk&qS%{pbORUu3}kn@Jdl5NFdgBaXC*sXFx%obzN)NWuW(ms*v z&O5QbajTaIi)f^U(EuUX78MUcS~&(HSp~sVDdJE7iX)BVJ^k(53XwdPe(c;nn9pwS zN7J(-sSFk-W=9=bvdy=o*vU+7#d7TrP|Bm#YHg}AH@|Djb#-2awI}PY+};Cw3IA`C z|2ImbdI1ZeSJow_h?z~}Xiyu+<|g)gSr(NqtvYxMw;X6#X?m|7hS9XIkbxdj=orrD zx1$9iu=~5A&8o3v9823YvVf5jMiD!tk;O;OoxJrJorhBZ)of!?(x9%1Al!z%F83L+ zMkrIJLE?qVVA#WoxTx{~0~xyHs}vD2H2WisTyI|w6r!F17?DeJehgSYbY_S9a^1cM za%MDSp<*2-S#rEOtUIE_^3hNMih$5$IJVkYfQA_jZ%moSV-W5dY!-xe3`SU-AJO=`nvt>D7A%(W#(9{Yuw8^$+oi3AaWOhK6ZVf$ zyyi&(l=u;RY2l@tOeaY%5HYLN43^f65-wwkQ{=2HL z^Qw4>LrKD#XyY=9s6!1pKh<0eayz7wQiP_Zm=M#8KjDQKg|wb(lm9olY#v&xntw&F zFNtnzb%v@nIlar-Bu*(KCQ&hQqQgZ|8p7!^PwINR!n)@Rk3RM13vzBMHllb#GBDH; z??}8@QB#AS-@w@El`J)g>f#YEzBIx6q3Lj%sYeyw(S}Wenmi$QZV*HLK<+9U0bujw ziX2XvAQQx(8LP{FAJx;|$^GcAx!Nx1lK4vvLwa=MQpa)-NaHm3tNl+l{5Aw;+aagW z_};pxRlos-oH&h5%*kE~XYM?ug3(TwBQ%|uKiGx)xTyKP3P&u@Uvbb`g-y$4_jQWG zl-S-;FYJA=-GDXStEtquQ|2Zz{LVP&2#`Fxkm7_tZ>A3HwX?MOf>v!iLPNQzmgl<* zUG1fmh%w#k_458Jj>B*Yw$qdJ0L=WCFa`2mN?`qFt^~ga3 z<|<+B&3==5h0&_vp>}s?u_5T}gk*#g(AnA`qbBCqa@88Fbe5K+qoXGXWsK5DK^SQ7 z8+2;Mt&Y+fCndH-UKxqY0BeFYFsyt7UFu(1r zlf!LnVjSnYl$6-PLI+ZHMz)jOYU*6k4G8xoi4^sxMY0F9%L~Xe?3B1|#a4$`OT;n; z!I~(`JlaA#;1&u_8erG;`j2e)VLtUn;`G@Ntm6Y6IgIn<>=Gw`GAB^Ox2C835Xo8ReRBX5mu?d^#^L~KPY%hsE#c=yEQq8;k-dkv@j-NdWUHjS}w0R@T9`4qd^Uk z=wLP3nt&zu!r)ujMU^7`bm^!S>ZXzF;K)3qAa? zo!_}njExtPabgQewg>woG(GI0)oC#q?d2is#2hXM;mC*boRGr`|?-2Y!7s4m^J(sU8T3&+Cmq= zz8N2XBstN*6wVNB++kzyr$JaBsN8W)PGVL@_j*x|_Q6e(@yeXqyTpu8QI$6s(x}2= zMZvbelbweD+W6l_{l-hK(q=t4hF~eI-CW=Fg$l?OQpE90epPk%SG$jB-IiHxuy+}+>H>K}Q zsPnAlD-5}LUWrlV>?=Z0z`61$jDoF7(8gFV(}xvd`Se$Wc$%-0!tWSu6!dc0gVhl? z(xeNzV1%PE<%WEd-}S_5tZH5RF)YLaFS?UhE#>YHkLtPhd36>I=at0*w*AHG)Z7ei zH|bfI+<2kM-p?s*M~KiME+U&zO5>i|>xX%2S2WN)kR8ax+YT{X;Se8<&7r^vE+c^s zImdw*<%K(yF*FzH)sop!EW=(O_m>LhXjFtWquOiItl@;SfJKJZ9av;-LD`R^Wf3}l zu~v;*PPXz{n$rj5@?uK0N-n8}4HK`O&=g?y1JTD@H+!>vXh+^GgtDzB_c;BOxr>#@ zFvLv9DZN^HAn>Q%-hRkd7(3Fol!iLSdqG}Ci5IQL5f9Oz$3!>~(dmqp377Y{>k~C8 z*UQaDv`zFUzB*!qR-NqC!kML{A=@Hc8=%%CHv?gmaJj2X-36L<4L$-t0+1;VojK?PIaSX}-i~-VMvQwEykj3G9GL8Idl1aa zT*L#xYhn@rgjn&rW2Y*N|`0zB?+e(rsZy^hSyP8KMz!_t~;GJCt*yI?AAhrv@9ARN~4u)7m? zX1C*#f2OHIQ-SWAeHq@J}6}z z?6iu}3V@5T)P}I$o0_JwW*&`94UM))GmwNv#^6?EUM_j+#sIG@MzKi923&1ZBEoxU zg0@9K)m6tEmg=dNKp2@Zpv&`EYJD5YMOX=v40_5J+cWY8g*QUb>L@Y|-f~-A?yc)m zYU17aZJ4d{(g(|cf8IhZyT%)pa$t=00c(T3BAL@N4HReRD*FdUOR$vA@`zQonRXMt zK*tu$`o{6^ZDQ$Wm9>rSPYaj7-5+VRkSt=8N2||6k1Q5%n7~kt^vGa6w@C#36D3x4 zuuw1yGfq%UYYAJp_$s+K&-e>;bK~+Grr)eu{^Y%b-K{m9pY4xbN2RtpoJynSDiCB5 z_V)orVopwDFOZf-vBIT$?J!w%|05bxuyfg(U=lf^W zDxJK=MQu2evcr|9DT<2FM94^E18W+ zZEL;_8-!?bMJ3!5v_6Y950n zzc&xMz~FYSt5^XSNW1WcvLws5x7}K+V|h_?n=B=1!zxFoms7INMI%HnZd_7m^=9xs zJeR?HRhY@-ftwdeTqUQuHNjF%!zu+1K4lF&eSjd>h?9E_Xi^TDGiSKjtF$CqXsDIpLK~q2> z!_E$B;+#IPinm~?36|;a?`z{eSnF#9sv6_H)PCJ$HXJDV`_W=Bz_GYZ5?k>2P{JEzq+KE*%}qiw3e$_W z-t_X@T$9P{jV$cw+OYa4D%W=n9*dSnEBkR%lb0AVs4#wcX>xqQfK(wSn+}urjoLc~ z;}%?sPfR@0{v}6iYIzjzQ)Kg+QanPt%N|d+wKAtLnt0V!rzEvVf2gPy9BE+g1oXvM z*hb-+6mM4X7t}8ZM1M&0^x`VvE&|MGD zw&eaGWtQC()thEeXp+Z93?wUZr>-V8>f;IDjuTL(x7SgB@qKM;mpngOXaP(Pidb`< z*5Un`pSH4n6|)b<>poWqm%c?ChfJLE7cBi1SGhNgC&uBqkn8DeFW^+7eY}Q)u?=WD z&XKTWmf9LGz(TlhKo9vB&8cPEKL-`nRXYo>;=J84T((quSiUbCwo*8(9>fLvKD`y0 z>+g~mD|)-iTWhHWhFzLmyOD)N)dL8OV55=~jFP-s;C<|PVNVW9GTmM6{ra>?MHdF7 zA9N6~a?K=qsL6MleA=g*(Bv*O-!m`|u~x}c^$Nc{0>=9jFC~*ZUM9%9 zUP4gHwQbNj=udiVdV4#PTwx?VxGMjYbRxrJOO76RK>G*>9vl%^plv2qig=2P zhks-vn7&{h-2+vhHeXH_7=lNA^6?uJI)ZT8|nS%m8vX zpH{^N1x~|h?rl()d@t}^3o1#ufOSbkBGt)_7{ygbtp+b_xiR3oFkKAgiLR!i>feoA zVAO7cXQ+u~(67EdJ?%KvveWe-2~!6H4T>1ScCGYCWi5b-$KCpIWYz~VeE@;@NIoCfJ zGl7Ue_*{a2Ji+^se7@f4MM=BZk5NV(DwJR`;M#5rAtrz2##Z#bJ%*^D18|9GBHYEj zM0x||(xrmN5FGNLuabt+o37Fli=HxU2?8k+#rR%?+U{frMXM~XmHX#q(S`z3(gvfQ z2l?YQst0@Ya>gPiBiib%b&7UP-YkHL>CxZhQUg(>5Wt3nD$DvaiDwWhil89~hxdt0 z=S%FbrG{Ofh4zM^zOHV4^l97o#$9t^kY*#@rdZxMddQ%~$MZ6CsnJ=-`TZMS8}v}` zpx5FjeN}%B7-WgtJBo{`*g;~>iw!Wb{$YJ7@KYWT5mx%>VokVI;WJ7$vFt)T2Zp>U zi=ru}^<+#%XLW2Nxw*l0N6$Otaxe#mefI)KT6#0s?|u@U3+*me@e=CbZwE~BI}jW% z@<|N8<-p_beQFl+pJGV~;Vvkxs_3Ll>`AYhv>uNQn-A|R#d(QX%ZOZX^8qP_CUC9kOs zc6D5BdXT^0y$!1>1vZAB9oqBCKJ}8`LA>hcjZ4#($7*;#$fRJF#?^4U+a;?CJ1@CO z?By@pPae9I;xE0Fw59DnpS+o8*+uD+>t1tZ*yHq)tv?4t@|fd1RmCBKet*KcMD)jk zy?7qS(_-F#O&-BCZVp2{t}A-mz+tl|PYYoEs&M4+*|1*8;wF%`ZB#F8P(`@8y?Dih z!>nN(tLvp+$ZF{vG^Jm%3k6=;FCX}<{Gz1Ux&LBi@a&W(x=SYBTVmUGI- zuH*@@*X&I=6re`z4eQc2x%X~D7x!cEDh*3~JW54m2X#|71FFxU>9GWfDB|$+D`%uS zVEMtO2Y2*LeLzK9sjIfxenW0ENasdQ}ZASg#bP?ALn zp`A$$)T$UuI$km07pnYeDU}HQ^v8a&$ax{-KN+&2AVNmgE3Os({#f)m4t+*1yPRku z`>|jx5;Qxef$N%lh!2Uf$m=Z!8-*As+Bql3X1$Gu0@T1wNZSy!UhcXi2FFs3j7L4An3#zFE-Nx=~;I<(=v_ zMKkVYrbOdd5dy~qWH{c};Tv|=kr6GCS87*7H>j zDIq&r|DhTksj`S?dDlvWOM{(D=a*b3w@ab89M640%8)jOZ^zi>1W|mU;z6Y8yIG7f zS(a62QYyuNhl` z@qq-qkP9~(ZA{_8mFh3Ne2p(DWHEB}QA2#Q9UChcy8I`>`~Z-p0am&}e-8%MRqkD)fQYEgA1>I ze7H-3Yjbd28V|+fIt@M&g%zIzQud*Cz1iQPMj_i8Ki@^=0-rI16`lL|usz_xP+txY z^b(m93mZ9Eh<79Mxjmb`sr`w6X~S0u`trlip=7@`CCi4-ubJ&R-V56Kq0%YLvLjUJx@ey(ybe< zTZ0-G%<4Eq!+7xej;XqwJFDft)ETh8{*YOeJb&pN$e>hgY8wH#FBHEk;qcSX`SC`B z+GAB{C!J?z0y`bh4FCotHjBMo?QM~|<_~wHhZEsDM+jxR9lfy-I$FgT2Y&S4N2fN< zbuf-aA*LoFLXNWoJ&J|ge>t>YzX>qN)h}!gWt(ax=A|ilaU5CiIKEB^! z%b=gM`rt|rU}Ve>_jTC^)J)7rMy3T1DCA47)+x!mv2YuSJH`sM3w$&U$E2{KVwvJc zVWX=ew6w|U8^oP_lTTG(-5Ah3NaLcN;HGR6g+d=*T3|&j7bo~h7#g2b3tbmY7Tu|@ z?=6@R)+{DubQSe)KJ>geD~DJV4&}MBzYw*^n@dOvULCP_ab8Brrb^DTuI$1^Mf``i z2>G(_e&}zbE5fepf zqQi%SI<^gT4u^$(;Uh~3L>X$RfS1^dTd=W)$#>2Crbpt)&RsvO(1G`!IZ;VXvj~Rj zGHwloYcCCFKfHv)6H#p)yyGhDohst|!9KK6Fnc<0lu@GKoVqL@9R0d47vRb(*DyQ* z(tVZeaLj_t!!R7D6*6SxJIq!`~#K;MGs-*}SYqwhFhmPfX{LkVt8!&{m@32tCKdQ5;|T zu!SaPU~0l@0)vchk94ei(PT@tsB`&3S2thj*qAiW=m7t?q-1}Q*@1hv{=3I|gyomFFfZhuMf}b7_}kDaAt@hfvHLzY-lI+UW{Q!( z51)AVGEEg?%e_V`R7&yK-qS-%d-9-|Z7KuEmm5F*m*aYHVTZeSF$i!^25~80khB-+ zVUj=(*O3TOuWfsv|g42X?%bD*53|sV4i_D7ehY!k$2LO zn#v`BO4;xrMkyY)%X*DL>}5xgi251o=mbMsfO0gND98!ZN-qy;(eYhq%x>C+DUD&C zn=bLz4VIof)K^Z3Fuc5S;x8@uDel*09jal`NX@tfV&4zBVDH4Py0a&Gg38MtrfO+Z z^j_VO?dy`qsy=&}s$vOe&n+hA#d4C|lk^8=jS7-qt?(7%UGp$2>q6y4=pr9lh^AKb zCPMTvZxF`!h$l-UXu>%>Q1M^#D}{sL&3bV`1dQG+qPE39fA9nB_2dg^P~s=%aqmf% zObCMgx~Yaim*#l?R(3bO#$#uXDg*Jbb3h(?4nX7bE29a7STEKS6#t5klcS1zN4`<{ zL7-X<*Bi{>5o`V_Q38#eT8#pbf^0%_ z;|pU&QC~NODUlSx;_LjxHdy{ZM`vs6>K|O(c5&*GA{WhW!f&}J zf6?gJ^bb*s2{I61vb<;0Mb+{}6Rn%U!PjiihZjMbO^=rFyEcVSK7)={#;1OW{PtX0 zylD3OljYSH?OJ`&_Uh`3s?zmW> z*bZT$nV)OXT@-h>YTk!<50ZpZ6gnF3U~j`21JAF6fR`!8uU&g34_X~9T4SR%C>s0S zqF2_2lTjus0ool@d6U%YL zdqIz7AiQ=ltpl2bd_j};54mKA*7WB*a-$-(?0Ol{DE~a0mxugUYrOCa9|)1HHonOl zc0V@M!}dk=7X9E61;^qFDBe}dhmA$EpThYAmu_!OXe?BChd@1#TB_Z@2-^^S=g_$O z)f*@s^VR5dFszVe7@aYkW^Gd?e}d;{_(uo-jt~Ea4^aW^@AG`w;g&7wbjwx&mjf~1 zd45~W@50{!>c(FH1y%rO0OtU!fc1dI-_+xl4FcByGl220?{mv0fJxwX;BMd^!2HQS z@)i6k@MnIGe+m8vfkVK}z+vDPpfMljc`tAu@Bv`?ufEbPTMMiMz6V?gYz8g|4gwZ# zE8Ij^1E0UgfC*qXPy*t3uj2RXz?;C^z&pUZzQfY&7mKF`+*aHg?}1u;u+xCi2uMa{-!K& z9C#cE;o*K7cm{Yj;!ljAFMvs43JBrhej0cIcnNqJcndIpx{3FIkARPX@H z35K|cb_fB7}Y7dQhr2QdDtaG(8K z-~pQe<7eO|vcO2hpMsm%4U{APy>Op#E&f_Juo|#*-hlf};BDY!#DD*K%opG!@DcDC z;6{^pmyKbr09%25fTi~s+{EL+iHLvZxLZb?1)L37_!DpwPXf;X&jPOj=KoaDEqe}l z0eBxU{`yJCA>cybVqgPs05Jcbz)jo(+#B(a!~KNEvkm;m@%x1Lvvkhch4%9J9%#%z z7r)Q*e#SpC?UvmM+y%S}82|R&m@~kgz)|2X-~{jl@H}AQR@?x23!DL*37iFN0yYC( zfQ7pSZsG`VD{vd|C~zEj8nAHJl~8}+e&AKW_$%k|=g@&`fm;FNzXbQ0W%Loy1sMMb z+_wRD0{2Dy58?h8_yqVg;(u}@c)+K?XMm+MQNcU~CV?ry_-m_>$G|#ZJz)Ifa1)OM zCnEj_HH=T-L*Qe;!k@L*En5k+0Ubai|B43wZu~9*M}WHl3vc|p8u%;sp+A7lKpwF0 z@5B88@FB2bf5KmW0Kb7VfenC#zaQ==eg}O8yZ{(~IoxLfD}hzOxxi{*4X_!w2{;5; z_{-q#1+D}JfeerbMu2Mo3r9EO-Ui$m@!d^W=Yb`_GGIBd5;zA~3t0RN+<9OG7y~AN z8esl(?*aA!KLT#?2>!o|@W38mA7K2~;C>x=19&UqpM?8E;B(+h@_&T(11uhYuLCY@ z;4@4cuo>72>;z^2i$^z62KE34fg^zV(@oq490l$H9s$h1=f`Mk;1KW=!1z}l#P|X3 z16KWBnoqxeAPi~;?&LOlpunIU2SPiTJ%%ASHz&c=k#2rzks{>7SsV)4_G>P!+k&S5O5Os2w3t*Y5#k`I}W_!{XYk__?IIraRty3#oGrr zaT9P5I0W1bSiGZfpK%0o0=N)}`JcmYDR_(d-wi+F9^gK};+=q-coKLDcm{Y2F#iom zZxe7Ca5-T7!?$9+0Ge-u+yab$;E&P2zzN`4;7#CN;631d;6o1!e>vhBu7GU*zXtc_{~B`)cpG>Z_yn+Y--7=c`2BtZ|IXVnUx8!5bAW|^8SamP&w&f? zNcbOo1^)_!A+`ctKyQSda9v>~SOu&D%)b}z z`+*06CnNp{+}8jVk3FCVfFA+302Y7sKY||Tu?h4tV5>(5C~*{c26zszcov5=j%WUs z2C1df8^wPC?$O8m{}eJ87y~Rk z`Hur9fTscaXCnX4!TS<$;4J}Gcv$=c_-!}@xAC8X`x)Rl;Q5HZ=@{A+xDMFuQ356I z1T4P!k<#w~a4_<}25zSD3DEUtNxWnD{W|a#@D5<n zL5T`r{^mz&{)3T!?Ox1Z;CA2zz~ZgE5B&?g1iS_q|5~^wf!)9ium?B*+zcE6Zud9} zdN=Tk#$Zlp?#1u>fCquE3&(s3hPQBx^Eklzyb|%rV?D`>>lxSmjVSy(aK8t< z4}2Q&Z~CWb1CPU?#B;z&;3MGde}?%4Sa~cgsfE84Zu4JyKlG*sT!G&mz$<`-I~VTN zKx0_`{$3QW47cHyui)3f-vivVh=1&NDDpoFx8dQh;2#73Zs6WU{A0fdBLBFqZ-Gy| z?XeW?xEx@cT02;nINWmvUyxEt$dzXzjm*TCIa?>HaxBeiy! zft%O^Sb4rGocSL`*av~z?DFM)Xq!(8^}$d2Y48;boRha8~|>O_{ZUX z0$BPmG2LX$B zkLRsKnc{MF;CJo6h0YGV4p{iF;x9)S;%wj?U@fo?xEQzs=m0W+rE>`G!@zC8?Z8if zCjj%m;$Na~Jo-V2NslQ|;s{{jEi9>p&qn?hm(=2yBLDl3Lwkd!>YJjDG>#WBK!?c z!`=gI2HyEM&=UanOcF2l--0lf-ctzkH1G=W2Jjwm@Y%%w_5T_918)KE0q+AVo=f~| zaPI?d0v-ik1Kj_T_@DJW^bg=1U_D^@p78?u9#{!%1}+2M2R;OvUxZ8rUIbnOGXGl= z?*#mx0&e;%tp9+ecMaUvdyIjuK)jWJ`QHZjF<=?eIxkA^GrC_!KLSU9+W_}(lX#o) zdnfQ&1Ai-icK}_$AkgSvLmI>$U|*Ep#J_`F0!kib(4&BrlfQ2PjsUks{P$nQ`uV?O zuL8UWSoqH){_7Ec`Dxz`}2Udo!>V=!p0XLwpK+5%Jysfw;gDU^(6X-w50v z0v`h}zm75iEB<}rzZt*pS%lyG2Ic_pdW84kwsd3vn18YzYXs7-0W<$036FS9P40T6 za0qB}d6L6Gi>v*(PRFgvw+tBEIWiwr50c}6M4;nw+$D>0o7_o+ z`&h#EhOmrtuWzp=H^L@64^0E7rAB%eSjPb7auSrS+2k^DkhT9S7DxbWlC8iJ_qR=% zR8BiF%fW~=3@Ec~a>H@S z)ja{vNdQG7*$uR~@{j9#;Mk{plADZw2oB~WfkJZ4&FT~*M|_U1Cg!- z;GZymAr~_FFZYifOW}y(n~X$zQdF)2v^VKGPs5N$!e98)gI1Ii`&7s@3$u3VC*l}qxxa!FoL zF3Bs(C3#D^B=0MiS%ya#BB==wlBG9=A^E$NXg z6=#NIP`M;o<&x}HF3ImIm*k*wNp4my$*syIxm~#=cPW?Te&v!ps$7!Cl}mEYOHwkD zRpQK$oU2@t)ygHgOt~b7l}mC=xg;l)OY**QNmfILK<|-IRbii|{{>l^W-s{lUqI+k z)O=g!*$~MpfE_$kr|W$6Afb+i1Hn}0w4N=ag!1*iXlBf(CAyH{F6ib_-dLnIx z5@|C+X|r7B>ktVA3z8<8R4z$Hxg-abOG4EYVM%_XToRQ!BZ<@*N~F%naRhi$!;+}f z86o*=<&sd`AYGFGQ@JE6bw)`3ALWv$)EP;n&QKzCMksYom-#zHVxnfKPS=_0L9$Yu zBP8c2m*hO>oFg-SgrrrR zLnJCQMo2o8OLDC^M@Sx1E(wK&R+_Hwh^!bQQLz!!!Db2!6&g)V%`MKIa*F;oJ}HHT z{T4~Wsr&>+IJGlU&VgpPsZRQy=aX{iH@mjFO#x_5HvSov6M&!|YLh`UUks_80K<@S z9<;b+TkCX`yay?#LbJQPPWr6plX56GyRCK7mpq@8gBb~}uG6uCosAqwR|1j)9Rxi{ zIqt-Rj`iMy^g?lW)JexYpL9~(8|$PTWK1X-Y_=5jgJAoz_g**eHCpGYI*dgq}Zz zlodaP8+YnmxRUz&aL!j zh~YKRP#h@Q=lF?6U)m{7b@NQ*+&T>wYhwm~tOHK-A->a8=TGJvBC&|}?@eU=4uI;A zuq3HHii896S2PUC3mS%mvD4Lo7~ntf|GqI8d@bxT9RNx}!stzICw=074Amo{hDd10 zfea>jOt~Z)HwdXF5k3$>C|Hx*fB@G39F(NA3!%C+4Lz(*ruHJh5Tpb$p~2`)appe+ zX~+bls->YAK3*uk%+p&D^-+268c zox2cBpMWtr#nyoxl}1B^(r}D01S2D7l1qS7T+0`m-D*n*-Q{XvYoUf|Vtf=xNdxEZ zgcb15VC_T8kkAO29*{6ufcJm|Nol+%ye$ziX;P#)u`%Y0o}Zc#F9&9*5Q}``f5H(p zfaFM+T$8&xR0fU>Fs@z#AOy)~pfN+|GO*G<2_O&&<17pWa|is+k+ga zXP$%!EQ~>WJ8kVKJ_(~Q9*9PHsE8yMD3RiW^am))8=f3XN5j;5^tUki^h0QDhZUQu_%A)R~ve}Ik)AS}regw?v! zb%w;ukenGj==z>|kZcGZbTMlrmCnPtcagLvHe`vN&bo*+Y`vh)G`C39Arhu?imN&J zSV~`|2{GkQr2IJ&*5ORF-}pu9vTRb33xl&4* zBRLOPm>0QgH4q7#Xkj30xW)5fLw0BqBy5d^Nm#>h*e&*t4fClqOqPUAa26Uy+GNuO z^L`}?P4g?EJ0j1dWwO>Xi-u#voFfI!lCTLD%ZvPV8jOUEuvjo_oJF%@+jMCfBy5{+ zV5CHajvM0yYWcIVfb&+Di!gOM_21hWjt zw$w?_LvR)y>eU_)ZKX&PeICqpk{sT@MgQId|09W)Cvh5Ndk7&V{}et1bQs>adR2-t z%!^fTs51ojj?WO7cfC;HyFp%1CvR!JtWI9cVyzma_fD{QN2|$Q!@$e|&UET~z~sHO zCYL2)@Md@Wb_-8;h|pvg*CNTSeL)Z2I$P@Yx{5ouPPgW~!%}K;VNRTi6d6N9WRehJ zJ>VA1LY$6`+v=9JFEp)Qx~6VnQ%o!1Qpe`G7xcO>X6{ zgih8oJO=;!7kf!aKGHsQv&e)V0kDRXuwpCXB7}0MxmIwh!gl!%eaK3_EUBe-l$J6w zVF>Ncp!cvNk`PxXA%gbFTpmY6ZT4Xf8b9`|9Mn6K5IKfu$c5ESLR_7M2)Q&nrtin7 zC9F)ADQdcy8Fp$GgS31XjFSL{8_C6gD$z-JqgF*IT@&g-@>=kq>!f-#ruidDbBKf? zTHGB^{@B9Avc-6$Ru(UcSf00m8;bmiSqCX)=7afJ%JCCViy3&hd+ z`&!_1A5zjJ<2aCxPR?V(6{+eICFk)dtoTil!?sALK+ao0Tmb%Nd!)*5ZLnCoun~B_ znbMPV1tMdblUydw4C~38&kR92ChQ#~w}>-Ca*uLJ9#*au8a>aFo&*|2G%sSa9U!6^ znn4;i!}o>|BT0ycfo6jV!*UWSXoAB@_F=Es;#R=_Jjo@@In$|l;hUGemq7UaTAi+^ z05k;&TSh(l5u2@`zpQ1M2oqp9NMQ>E=i(uTlMtbFO)V@PfmQ$PX#1RaM2$m1!29Kg zfaPwVYdHromY!SZZ|#?pg52#3?i>XDcDauC+68J1rL(-!6Tgl2VNr!LJ|E_x%}6Xhw%tBbG)fqO;qR63 zSrSV&8-}~4&R}myupEgcn~RbSmA7Q2#O@B5)ivE_x}$$g(}!6Sg^8xH7SD&#OpzAS z33n$O@>&t4qkZ7>dk}f?oul=!P6n8z>g2N4kr7)!_$ky!GbgnjVR2LC`=d{ly3}kJz!0(U5YcWI=Py0uob9$ zv!YrQ?b0gx%}V~uURbKn-!|pW4b@l}$(p%E%Az?25xi_M<~M%Pa$6S8^`cT)U@1z- zkw!NwitPc(p2&-Cw%Vx(dWVBXZcS)$Emdt5rp)>;tl)oEtJwTw@=58|)amAPWbBpf zRkAPWnuda|2|}m^C$Btwz&FL zzI6Z4M<(TKcqqK~AihBdhd%ue-Lk&~PzRC|fYyPor`5yS)#%nEZrKokI3(|ja|g*u zK%>*uEYZWnjidKVoFNj8o{yss)syHcgO?`3{563r$@7xd2nnMx|EwhNxq6VWDC%LQ zNazUscS_I<$#vo!BGIA^ll({JCgln{Z6@%iJD!%iQx~Px$|)9`JD{XWKR62OAqs8QorgxS+~Tkt@Yjb zv|HMB9__Joyb9*)8iwSggh2<#|K9yWcmT+YWIe(xY#IjoQUVQ;SfIFRKJomNR`*$8 zMoq)-=P-q+5p^umV+lOiFr-T*P=qqwA6Mmlq=wxjyYEu%5fVeF?rdXU^6Jm@;A9whwSjmDts6ZJ@E zm;BlS@2wIfOVR`U63Y6U7gUoWOeTwRCXO61xFa8OcL9svbxI1HWf30%&_^VXer25J z;G;zjdZ?~rEGVnZj1pq-J?(GS)h+OHSN$F1CGC2@Pb$ic^&p!Oxw=>aDY5=!h&ODB>AKY9 z=oQC(1;5drB(DKj?S5RRgEffef+a(`2VRH8J412=(2+vdQS~6XD|pa#Og%{05RH@Z zpyx+&OuG!z30}7VtS%|DK|7N$HL@*Lu;Y?m3-5}CCSk$T`Lb0kP~0sR`WskCGT#Zk z6{nrJzqkjt@#<10k}+}aASo-C@5*8&`| zq?&2QG9{b29s!Ujb7lIe@!h-$T*hq}T@Hp5OlRN}SfehzpiXB8fUBgZ9c==GKl+5R zR~gn8EFZ!A?g!X4q*~2klFvMwRI8jCqU;dHzRw3E)!2EG&pn$|V+TcKiDQ5-fHPcq zxhzG4=Pm#2m(LAz&`KH8Oh38?U?YJX2Xutc^@4hkycs;``anHMz6c(4t_r!z`Az<8ab)!%16(VSxeF(&AHNQZ+yfXmmUP zVEmFW?PXYXTK?YyxSP&jkEoZKv2d^OU0IN;dLnIt#?DIyYRIByTRTxJA(jaj@aHdYzIRHFJ)&vi_zNa1} z%r&J(j2POwHCliLb49fBGbhjq9vxuee4TLL-6v0&EJh? znI#*A_uVIi9}+tVpi(3c0$O&uj;jaBJlg=a1U}DyR5qISNf4f71n=gHJDY0w^ZQj5zZo4=I=AQRR~GQ%&04bp0ps z$dK^+O`MS+%f*=?xmdX*UCJeyQEpta4M;+>8DYX2BJ2p*T1klEBt(!h2mo6^52Eq| zQ|L4jf)6N`{|s6;fAE7-yy0=$r-^l}smWsT-KdobIR^e>sad#|IFvC@4a_BOxjPsV zrT;T1!b<=WC1KNHgQWex#rpsJD9ZB)p^Y0U!)wrj-v>^0HALN*?&UWzx8DUYfk`Mo zR1#SE()4ENKaT+z0wk{i+QW3cr5+^ji8DuX63{SoeJCCo66#XXjN$lnjv2fHQ1X&8 zU1UbW@UcL)g+fbcC5u%%7V%#J4XT}$H4bWphH@TMKr4MibgM=j-RcuZ*K(P0+I~gl z#3s6R9XEMaPX7!>nNcZ2z^IggXQfO;GUMr8~arGc!QFN*#1$+R!2Q^5-bgm1m5n&IJuuKa}%|KTI zJV_;GsAlxqSN;3qN~i@0g9%5{2552U+N2&Nn}Y{kThxQ(vfx4273x94k~G%rEbuM{ zM6LKvORQF$>%G1K`75Rr2nSeNl5#WN1^^{Zm_N_O&kh#>L?YQI&O8Y}G_1v=YdwH4 zB;ONfo}^W|B%72=k_9v-T~~`ohGe%m^CX7>4MW%6>Ot~P#F-(vU%4dD0vepIzY>oO z$y$uzNRe$`x)e2LrTD#MN=DMxfhFkg)pdSBnNebynKV%zzS&(pODd5|3YbgE z5p-k|7?L1X)8ro$)&gw^1ww1GG#O+>#fFqQflYF=XOpspz$PIW+8XUtSTvWfI`!f@ zOZJTSBxUgshvWm#COxTa5^aV=qmt6%nhHNA&5uZ986v3xr=jgm`1c}MwLTeigEjCpEcQeQYTBuoFz zsOlE2HHkKHP>h_L08@uw#I%Gb;W-1Gf(B?^Rj0EIEEf^|4anM6(i68NLFl{{SjyoB zZ@QU06b%h;Ns~SRj|at*I@YhPs`CfCOuChfIP^R4co#qeodj9ffAoA$0(`9iIOkuw zW!(UV2gzOH942`}xg^ghm*hD>=N?@zibsazZE+5h{72=IOdXfeL2^`_ixlQEggYux z@=3mVCjX{#ndV38OcF{4QQRzWGdZnMj>$bL5pyK0%IU7<4=!=5Wn)lh9xtmu$$x0{ zPb!mR#wDpij)av-C1r(BB~44g{BVAh)MSJSE|*kBlA1(Ajbeh|x8-+jp>UFn088AB zcu_&u)#_on8Jz^1-@3(UAo)EYsOmT4$6@iyko=K2hb(4OljYa!elv`)rnnRqv?T#TnK!9oP(WHy}1AIv$cx84^Y~$F-gIGyDx^x8dUTysr(%cf}C~ zhI9A%XiITXNQ$0zUH<2=*#c-^68^me%qEggl}oZ*mY)%lGsKx8X;UuACgqY) z6Nt`pZK^UK{y_$+0i^sR49Jri@ozDZO}a&5M=f?G0%(%axvW-U3SPScDl@5$i#EkM4?|n$WMW@#nyn$)~2;JZ09!7IF*EJc2PLkt~4CZ z7aGE`6bCV#4gqK&63);CG6uoU27j#vBiR77xcLKhI+#Q{PBV@!7(;{Mg61TClmLh* ztGd#4U|uX9BP5ptEpAU!oerkEl0Bp~xF9|Zw^W)PQ(!)g7ColW1whsutkc2dqNT^V zt$}%Mk{n!^+4N!@CQ3R1;L^!n92U{JTYg^3pR5&A_P&-7wNM?#*h|8@7g*|U!VF$j zr#lPaJc8s}pxO1;Nymhpi<-;QZ#Y1F^bcTq0Z<{5w*hv+;zscg`*4E@Kp&GVM<&`| zbghsqGbCpO54z4$4-)oex`$EECE-H4RoYYj*_(p^vLiVp&OFI2fTly&L*kJkd03oz zlDCvg!oP{5!RfjLK$0X|#F;1I-^S4}bp2oAks??j#~ zCS|r27x7qD{)ry4?N9Z9%`)BV*`)kSJz$eC7luwX%!1~+9_f|NgD z3_82C5kC1yWr88UVD^wXq~}VkJc&9p=3I_^de?5am>=aJkLiEI^Y*y&$+o;=sR&7g zfNuhk8UqVQU`7l33g5l7(5I_(w)aV=oU~Bv;=_Pv089&#Gx1;nlaHiJoEefH<&s>j zT$0_&C8;TweCx0~wx z^yS@%@% zWD|T4Gvd(d?#G(KKUa=gliUMn7tnRTdXSt1@MZS{bvn2vX{dC9Bj9UdLzdX$vWtl` zk!VRzOLI$!1C>eLEP*rpBj`ufc?^Wfpx`7A0t`(0j<9p@f@mfwy@knX8ri$y)HK48 z5GynjLX0FK!Z4G)gG_pcs{qWG^bmj=k+7iH`~A3{lz1pfE7^ie`!Vi8fa#EG>GBqy z?6{p-#SZ{TU=mib*=>n}oB-!-2@nZ|I0Z;nc(0To8Ip4W?NGYbs|QJ2@Sy85^&q)C zc+l0O9whz2Bb}pjXTWEj1k8~z`GsqjWxDvdVIi17q>9GlhB>SP*kYut48{k^2G1sC zDqxfJdN!#h98G>!;NW5Gi~)ovc>ri}d-v7p;2q<}X>i2zqmhkmZ(xQFiHmIw#b8XA z#V0)upr+4)ye7^p$(!QLkh~4ZWOu*!>vjI%AEHG`KLYt!oH>%ul}qv^@GIDSZ^2(- zXkE*Xn>F)L_t)$6V0{qViqj(H)&eEF9`!vNI%}_}uo;q!S79@Cx~^3ZONaD$FKloC znuUbJk1f}z@-o=#01stI-xhbxkupyF>j8TBB!PMmp?N#&C855^%$lA3Z!4l0*~e??9+qU#g!$dH`%pu{9u2WS|& z)~g2zZ-}Ue)UuUX>^j>qS47^r!I_6?!N{Wms+pFhuy!pFMZwtP;!z_fQ2O|mt$pN);59` zPuxb-!gw~o7LR9rPBAs(yLhEsjDdz26X}mYkFSS~!u9OBVb4y~+7?j@E;~S*rW9EWftuChE+| z#S!lOEFZ{P*m*9wAS4W>O&L~4oO_A!fHCd?D9TB{1n}OOgy3d_n!dQvwfHilq_fw_ z2EbC&MPd^I^a4zaG!I~~kZ{zWA|}_c{Iiu!O5qj;>U{|fOm!Rks+KCx@*%w)Nj)Yh z55Ee+Q5hZRu_Q@|kt9S=i?l}=<`{6Mw&=#SH#*7c{5rWz|C{E++YC}^X>LpLE4;0w z>twmE{E^&K~?F&um+{Xw^*%-A1K6w+EVX3s$ zoL-!eF!AYB*tnL{z&C+S)m5~Mea8aOorJLoyLbvVX}>Ooq^jon5~XUEQdQVso7XOX z`qWib*QU4P=);m>`syre@V8QbTS~oX5G(XoC1J(>nk1~O+LA2sT9!+9uqq_v&CdCC z@|oecVm~&bT3yet)03UHSY*qF^aW({3SgheTfU#k@r*`3%>TSIk z{QwqWlKTO~dg0VMT@T4G6tqs)!vKPjQ0Yl8^Ol~`a}Z*KMj+W5BG8pl50c5?LD$Xd zL2@K`&~>YNkg&vGpEt{}2B9vNNI4RgVc`;GC0l$!VM(vlBuH4=g-KW)7Egk8DrpiV ztk=RMthS3M!P?)ZNm$$R;|%_vx8=yMge$fG9wJfxlCq*IS<5hTt*P^O8$fdlKRHeJ zX5e)E=D3tVWLyED&)F2cfXH+2R+HGq3$SUY7aIb%8LxW>yUbou<+_(?PZiHyajLrMaqc0?M6B#?y@6g3SV~%j_J)H{viMbB{>Xe zOVIV8dXPLEJm`8vJxCr69&{a750b}%2VE!BgM`ITw|0};2Oe{jD{?nCCC7(N?g-dS zK`-S;UfLXY!|MU_F5_|?f3U>d0lUxC4 z9q777JxHz#9&{a050X2B2VMV2JxGoP54wJ$9wbkSGfVO|pxM#&fp}y{J{4z{goi!p zW`*_$MUExE4E}5nQUaA`xf$gb+bMpA#Qs0x-Um*rs@nfQ&ocvzBTgfYL`L?gBcUMy zp`u^3VH^k*@p*;;(TLEHkdOd@h)7WZ@lS+GUNWu^bjqnZfS&_xk-_zu~o5`?J>CYp?z1?6c2#_Bp4!b1+Wqc&PKV zE$InR*VDENz8vDf5>qpEZ79u^-cW22)VVB4Z-TmZkv8=>HSRqS#fqtcJ9`Vw-pExf zzwyjXpY&8OY=gQkN4@eggmW?VOlRkzzdAb)-Pg7AFbMsR6SepjaRHZJ1|=JpPpV3H zJoKKd{(U;Vv0m(3|JFN>ugX9@bfP=;SU>NJPjZQAC>_pebOxE0KrE}oL@wx)Qy*H5 zo>Y}at#8*~g^)YDMTuR4v8%u9H3Y+k!8A({vSBb5=C&|7gK%(h*+;DWS{3@bhkKeC zLFi(?5zre2-|jwf6nvZ%en2~yi_FsTIy2bD zBULgrv8iGAF(e_XE_T05P7r$t^5H1;s8f{1`rI$`31Y(`Z=;m%$C>K_KLAv~M#!C8 z;BrP`VzTc%mySi_!x%549nOAy>Oc84D_sjI%bk#_DgJ=V)1mANSYq+Vz4+%aA8O`q z^aR9eg3K&&eR?xRx|^I{dw>`bx+=T=_yvBbrZ-G@RXA1(!~D+5>nRXi=Y;0hk9!cP zu1C9lMRgx`KcI!~DMZ^*y#&=q3$)hFX`^UwtnVEy@K@7Ao9ho0j$g>*TExe`IrOhW zO>Evkkkf{8ipI}E*`3i`zqfGw0@l>-$h1`pc>>@00FUxQ^r+Z*Uw0!w>^_$)i*58$ zv4^}=>~Sv@+vlZXV>UVuVzZ&_**S_{rVwvphCwRKbf{u{4f}%-51de)xYM|zh_98I ziuzWiSMbNN!9{(VQ%m(mM!mr1ma3M(kB`(~NeLO7&{{k*kENPKZ?_84VwcjO<+lUu zw?NuTjsL3BYBjF6io*4@Q$Hx^l^)=6cj=WL?69{_Jr6`W1j@{NQM421&qK^+V(ON6 zMfKk+&WUQ~h7H8=CdVVpD!zz)Bf0h&<=l%kLB2ng>g^R`eG-LI$9siX-$bF*30@&q zN)$>pdxhA5M3L5#ABG8}5sh09-@$rV<;Z0s2-ccYGS=s_0?t@V5Uis}UVA~g7L>t6 z3S9;9laJVDNVTt2mD=VNVrnI?NZl%>Okaa|DRmuRR;k=oEA#-=Ga7eS?fAQDy>ufI zxlHYhpX7;YBa4&zrpX2V>n~zummDuP5%L|S)M}?Fi|u#G@nV1RQnBvqRmt&U=ecBA ztQGQ3lp5_7V$Zqcc(K>KRIL8KV6IdXM3ai?{9$fC(n0=Ct9yTWTC4s9qS$pl5%<;U zPz9cf#p1pO9byVn)*)D(B^2u9ST4R3Vuke#>{*`_Q|QO~gK^LzUOE@=J(ajO(aNc~ zBF~(7b9c zXP5MKsO$A~=#$R*(k@Ewgs11_w5F=3Mj1C?@OsW*$AX`eXw?kb$dn>sH9qRzq;eb!3Etb5` zBMb~>*a&IVh;Mf}$|1AF%B{g=CLFc*CM>r>eB6`uE-Uj;N+Jk>7YZTdqJ2y`k#<#) zke4{$)=u$@%)+Nfk|F=(!eI>6jYL?#x;SW{nN?L@36+ik?$~q>J*#x+(PA`TkGLg& z&9OaSTa@{gkooYU+9+6b=ZhCQFUm|~A>iQ3eI?UbpN5w9@GCqXs`i%^Y*0h4*q9pR zWU^0&{NjTWiwU6^vodBtp??DE6V)7Cz_%{m-K*x1U>#=j*U&Rj*omOo=_F8{8EK71x0NV6(#wo5^r@@=OayYHHnUr zlrK2!VHL_06g36)n_AqC*Hqe7|Af5!8kywo%1&NI=8F5MBtvezy5s!?j9-Gfawd6q zcVtco6Nl+?h4e~dF+R0O*Rh4*-Bz@aNV_UCR8$9M$laBlViTEwdL$Wg6^&lvz|@qZ z4SKo5a`;G2R}iz0n3g#H93KMFHD54dlqZkN2kYG9@*$fhKxONIG|P$0xtZmH>Ap5@ z7D1@Qiq5KCtQW+dvqe=U`a{{!yo1-s(}0fbVlw4hJ6&%jg)7ZYj3(mmyb z=GPPncI1U=o{$=j$y9G9HWTszC^gF~#AYW7rRI8t*t|rc)B>*%TbL-6TI>~KYS+${ z9D{T^ z#w_>|*9W5Bx!N_Gn7;3pz0Q!4dM^Km@#T<~`BUJ_AxjrcGw=g(`uHz#2?fbq z{Ba2LCt=+wQeyG`uJ{hI0kK%T#fw{E7slc-@UbpEs4A|&a<7Yo7o#>@Qty|_XYtm>lqe)X2L6CVa3x9`dH?r2St zyDK}jgvfj_p+3owU-0<8+>QCKU6x5=qIa-^ zVQ6zas)o{y66$EBLDGXDdBVnbgKQ#qOfstAwa&gA?6WN&3N1#%lYz-tCL# zGCBAAaKwB#ljOvkC*2dKJ7w@zV{+Hl1 zV5L=%UXSGFhM}1ULqiNlS~Dt&?MKco)lMjsru3^vHkaxRW3F_ORz5;-QmI9Q_q5hN zbtq}pOVeh(G*l_RvBJE_x7yRzG$$x|2{q6`@(ST7L_Ko+8r+3v9zXWG!3)tn=+tox z$F8#vGTGCKUd64Xdo;v&l0>!B5X(HO`Y5_u?^@>JZyVxgAVs+gz9&uxJuj3-61j}Y zJsuuIMljF`)2D3;6A{eF3H>T3T7h_tQUhq;ewGz8$x#>gG9`$Ckd4-!B z@3Ak1CV#7-M(%}SJfwMU*f%QaYKWQCYqUc+-cu^MkD^^sJPvhsAn8`9D+iK439+No zpQneo$G&6t(x}^Lchzj4Po^yyQu|t2lt8qASj6(rJ&vVB1LP+drH+TN64SEND}tLS zdX?Ogn>i><3|{#L2I^VyCTBRVH>|%;4uB2v$ssWBDKjS=io>HuX*g+*D-tqFg++)m zGwk^MINE;Bq9UeJxc7B^S_^)pp_u0(sw$@T$yQZ8AB_6A+pb3)Zl>SOljwu{RzqI5 zHI+A%cVS{nP174G@KImB*j9)RDOQzu0`kEq^&~`;V%rmiQcrn>m;&t_zRHl+y>cU% z*;f!KoDCQ}08xaP@?<^9Xa&+$&Zr`$JTl5Ye{&{oX6YddFTWRi!5QQpJM%v=O{x-T*>2@47uWr`QG3VK&tZ(#Wg(S2es zx@1MH4}S7J`4aL!Ig@0B6{1%Wyash{pUF$OeFN38t!B_b=A-J@K$|m?H$dIVo!W#t z`rXPK(eH-TE8?p*ff`{h^aA%SL`}369n*DCL3%0mf-O!Md?X#P(h3ArTAlQzdFRT1Zc~vWcy4^RWA zvktN8F4->TGbg)!a6m&s|k$q7={ zDsPOlXcv=Jr;kMK|2!LxA!;f%$eHIxn2uiAy#cwEjuIz0%Z?JP*(b&Nx#T3VfnKUW z^rv5d)P}*4JG}AZGmj6MHA^bD4B2_yw(umA{Tjb-%SlZ2+^5NJWgyR#F=&rr+pugx<)q2 z%2^^krSL(~=Li(a90ofO?1qX_Ych}2L_vz{6U-^U7|HY%2-hQxeGqrDKZtGJzsdbM z#ym(nnfJ%^OlQu=K;pleC`db*w_@7v%x&;N+R0p|S32|b?pI9|q@BzupWd5AhPf^i zQxALr#D?Spx81npx~jlRFZiPq2&}ahp%(1LL%a?$gQFO&j)k;m`piGSTF2r}F|9|j z87fBUH*0I6Anjx>)9udOHc*guGMDLYh-l6>?H|OQ>SQLQtohy|mx!DtIj%IHpTW@Ryv zD_RNVF-6BecOaBQ8c%grvfe}0SxFySvTk8FNGw#7Wm+Kv#w=o01W}^cLde%jsq4H# zYzah&qpM0R_j0inUfxz!Vx^aht@84gsuFj5xh+`bUhc#dFmec)Qz`b6m0_uiD-F-^f7BP7lCv9B6+Z{+5!D|o%;k-W(*QGKrTAj2 z6u8a{UU$|4E3AcL*klC)4|##lBCyq3JOfbJNeBv6e>L46nnz}+7^NqV9+4!EI;lNl zy@KR5C;dp$c=FPOtT)8?5o?38!$3y7fuQaNh*lI^2g#^xJe`B=9%o|KeGF$r*E#Vx zG3Dk)XZQfUe8+#^oWl=E5JiY>b;T5#k zE1638{7O<*W?XMT{0(2On4-?NL=?3k9qEn4Ms;cQ@>NmYA0R3$_A1ob(>+KZ@J3?F zV^%Y&Z)#Bq7IKrBR2be*l5@Aq-66IYVi!|wRSEAkQ_U%eXF&ufrb4p8>HH?6Ro+PK zOD>r-VXZ%?oYf3eT9As%p9ZY>Qi!wr3YgDrt}ggE$0ryE5J8AN4`qXJ=Lw3*8E(VS zZP8L<7sT8oCI?O)#)*h(v53)V0wsoKZzA7^OWC<|=vH z3h68*{)K3#A_L3bDx=ef#?XrTpdSW!R=Rw8j7uQ z$q8cXq0Sx8TPl%HaAp-TpJTkuk&c2mD$JT(jLM+a=8Pl_FaTtEGk+scsbvl_*v`5~So(#VEX72($)`YH`_NYw_Zz*L%kDGH?) zqcf^1))zITDn1V~U0xgCl1;`%7l|~IK_-$3iewL@N{FkO7#<2_zfY=B8sjR9_lK}h zNSFKM6xdv!TnJm_lZ#=?eR3siy-#j}J>-*{VUPOc7TDuH`7~^&Pe!JJ(^@BIVSTk; zhgjO$hltqsB?lTlY+ou~^|AP^E#jSD5F=LQAI2mWsbbW6ZiVQUJbs?MQjK)l&DZ;b`VPL z?H&Rgwt_bSDlf5!TQ+8EQEz|wa^x|-6<+!vg5bV zLN#PiJ+h4*yt*5!!Hgn8bo~B^6JJ=eHgT^FC@e%FQsu>$L%b<{KFfS1S2%cOi)Vil+|& zoZ#aT>)&NO`#;Ck2t+($hoNjd^0aO;5kMG;ZGt)*^(3|?7iL+k4ayq%nguh}Om?Ta zEFFX5EZLepM$Mpn%~lb>8Xu3C!pjz=pjXYn2qI{)9Z=TD#}mvHkL9xJjjQs)B>*%+n6Yn`leTi zy^$!CI_wo-kikGGfrF`%?}LtsYU>-4nf^?XRH$c=TsGPQ(z+vyVclz7Ptrp^3uhOaGULUpo&K zvEZRnO+8eshjeXRh<1@-kIQQo!%=iMqOW+d*k-76({v!6mp-Xdpr<%-vTj%*6M?1TKIeY9OX?JGXFZRIx_cwn&ZI zgAp%T4#R`lnVB=#=%A5NcORu#9mI#Q3dsbfoZGN@+D9j*?46@ajVjhC8(nJDZjI7k zaB7mm>>RBicGLC$1<^!eQK#{xL;cri_Cd-l{t{#^qZ!Iqv%rQ-Ps#b(X;IzpAe@V} zeX?^6A4k63nV4}?!tgZ6oU(NSTsMyUnvlW}-w2s7d@uCJ*&s+p;xnD|31YJ#_t`MT z7XH@Yr0N`%#Z*S;Uf?BW$k#fvikQ#QZgXV2WGP0sIQz2Ltx#vTsZqrmWj!5iXR+ly zi7Ag6`=J`LI>)IhVr59>RjNwO@(M9!_ll5HT)BHWqjoW0Ml!s;WeJ5Zp)8-{oq2fz z>`I>u4s`-yEr#$kh$YQiHC3hBo!NwmFkgT<)LAMJ!mAKtMNAQOZm^w5cX=bRJzW}c z1Dl^Q4!d78%3{a6AuwJ{qocE1#k&$;YrJc*dlJ`5J>V5$8xw_c>CZ0;LVk_Aki`wu zIFGw6;xlMjBl9~UsvCs0MwpzzwErvE%AN8k1T*eKe(^Uebfi;P#QH+Mx0RaY6=Dk# zg;I;WLQG|K9v`8I)P1{k-(Hpb4(Fv^EVys4rtaIV`*dpZt!Es|)l}zdyvZCQ2n%6N zxXRSqS(e3=kqJm_sh5h~?xkWPj{obcN~oe{y7IRILn-py~58ZGTkSw5kH>qE)HGG&HIvp~(J5pA-`j?c%BuzC!5%Q;h!zSTQv_+iPc6 zl^Ec`ys8pIJy=*(Vz>v3t4fT97)4@ZynJ@%^z|KoZvH`d5?c>-_9W>>ZzQ&(OCuF2%^q(k_Upt@ zsr_Cdb|_IOm;U^bm3pvm2??%pZwU!Lt!Z$cGxcS}SEl@)qd50M>P>OEq6NfWi^bv! z6LH9`Fhik;RhF!B?NyXaWO7owBvMkSJ^*N_R(df-@W^ zrgA&K5-&v4bvG(H70^XDhSF3@FG&3+uA0*BVnbuGxT=F#tSuIct1^hi+GDY}FEca6 z^+l{;u@>tO`KAqlL`BI&m?Xj^A(!)PLn|J zr-MCslRUVSJUEj){Q0xXl@uWlYz>Wg7hZ4>!;mkAA!{L8M3y-0Ak1?GzXPH@#GZkC zlPUGASBO2AD3sde6=KgP3Z;JT6=Hi5MJTfnJ&sKIvj5G_C^vux7@sfnl%Df?!qd-TS(Uq8 zGPNwGOT=u;@FW*kle#vi$p3sQN{dwXZODHI(Nkiz9r^6RXqPjphy|nUnNyx#az^c9 z!6;n|hy63pq21#Q$BFHQj^gplIoh~+VpUN2dm`fK9o+1sk|;i{9okc|g3JD?*uD_; z5o>kH@nUU|ZyBY=c!gLwQDj{S$z92rR>Zu!3D#Yun!1}{-DO)RI6K8TYZnUvwL1?V z@d~jJkZC%yOFGdRmBpq&`Hq%}JY(3SFKbrJdjl!qZg z7kd-(p)1whZ$A*LPqrT@rM(H{umPuOx%?Z%+#(iSR;n)La#m_#!DXeYRHIjj^@4op zO8K(72z?^TsStsPO@n;sO3m^LvAKytDQ)vOV(4;tl{2V_1(%hoQZv0mEV!&xm0IW( zVv8Ujx>CNZOz7HsvJoN#v2BnKI<#LQdI;I0kbbfYQqN_*2O_iBZy@*RhZ412-+xB4 z8=^17f>GxQK(6J6+XZ5WWUL9Ts?>jbg;?;AodA4z(ltSZCpO6?JH#eKzM4u+@d~l2 zi6UE5A-O9#(~6jPH{QCdR8x23t-Eaf+8M*VA{HH<}L9~t77a_WCUR8;WP<9X~n{q6G=sK}rl&zYK zZg)oQV!^0$)l}$>&TyRAa>!IoQ2DcGHc1rU+nm`{;8uugiEV>A*F;8}ol!+B7^OAw zi&6c0HyP4uUVI9KU$MopIB8B2Wn}FT?>=a9FZHPkO@Pd9MrDv;hZ9%CWN}pd(_{CO zGdB*WqnZKLMb{qZT?);H%sR=(Ak&#n(JnR%Dn{2jF8j2AI&Yu;ul?KyA*v=;$Cd!! zWJ=XTD8#hSymMP}6N^m0cKIt}A%CT+lrouE!%ha}c@0w2imPBvFChSm47p{J2AifD zui`!>gsIq>kdIEO7OxPyFi|K~_6jjQ<&dpUAvyusNl<>O>pz175uz}$F;G6MOeAMP zF|^p_g2Klxn=2t)iA{sNE2ZXnh1kMGq11I=A$DV;Q0gYH5YyJPY*cA$r74v^#bv69 zh4MM2fM$Ay*epoiSe%er;1yyauu4^_C0-#G0%LIk@_|)sVCeythT~J(P4|NHR2KU+ zKexs=Ti7rz`ER@el4m8y(#HK;MQIwkH6=ET!R($(3;8-L^|Dup zg|aKvwCsv4J6pB3SZnE5yE*D3to9SBO0bv59Ey8Bs~eeNZ<1 zLUboG?|g5@2RfVStLJwX`%IOY|$-R zz&C`d!74!OEJ?cuPCw%cE5dq1K7OT6@Cva3i9)HNULn?+D3mIDMQ~442)aYXD1Ab= z7)jF8Nz>N^6eCF*oOD!jsw_l9kjS-m49_%Qj3`Fu#hMx(B~CJMKL+tI2=RqRERk{` zPWRy!qP~bu^5XQ70p3l8sJ|E6hdv}y1K=cme?q9O=W2q>Ax(F%S&(m0r51RF*wRFy z)a_m&c2A;EYJ*pVGWfCug2_;}MJ1W)q_#yRndYRnMQ=qS*PUDR?pTv+QDs;KbbyKf9!j1@ zt|wnFC=m!Wd4YeTS>Oa~aV9{;C0Rn_>&6wV1Rls{16w%3N#{lx-9l%V;X3yY%9+%Kn7MvsK8VE2n|2+3T?Twic;l?ctf(MqIAn&1KJ)~`~sw%g%I-r9Uu>|MhWz=03kT>eG9>J&(O~;R7 zvW+9{@Rj(7C{}DD)OjGusO*d?V!_B(MkbP*@U_`X;?dAMqQP^n?=`4*(+Bz&qTcUp zu1e_blV|h8y$9tv4`Pqf^;IR>Ao^ZR#XEV3qq}BP-bWj1s%E_~HslBjW}QZ_vmbB= zSMCr&i=6}c$}2V8E5y!A6rq^Fd)>u`JO~eBgIuyhY$)WNDK*S1LbjAQ-y7A9fN&r- z(Iq>?7C_#CQVX4;EVk4ola;YQX5?os2maXXSUUost2aZ~kkT<9pSA zwtJ^9%@v|xZc46k!ln4Ajc|}0OtnUivqWAjgm|(I6Qo^5X&dJ4>=d5Jgjbgomur~l zZVc~n#>q)c7xcRk>A2>Rdfr@r#wk^SUpawZWse|4!*47cIR5xIzl-VcU*?Xe#4qos zLof>J&JBV&3$tguprhvgUHSBnKMYQ+D$iU9o&UBcx^8aeb)tzQM5K==-wQFlh>iJ% zk{x1Y$WK8^_5Y?*hz*50&GPu$_;&gG`TU_+@hyq{1eM?|k~<|)G!)?_PzhD#OZ|^ersY(2pVbdcKYS`2!ZCzlB zQ2k~?UG>*`&YPVO2MVs;+>U_2S-+2mk5+9qC zJU%VUoUUXum6GSWAjS)+TUFw#5NjijwzG2I zS+B?4WN5I)VsThg)B8?tY_5Ont>fR-TmSbyv8XtBxV$Ci&UhGsa6}!+&#V+wuu_<0EHq%Q(5Pq2)PzZySZt{I!#q%@Be)Bhk zV(k>uiIBAm!CAWynw5pXtSrRTnXf0B|L+^imitXg^nKEys{7_lE;X=Zy4{B2S%|q# zOw<(3_EXOpr_A>2f)6#HRFy^zY9_@(A>_`_xIzARy@qgW5?985k3^WAbVDZIT~5z! z^+Q8!7vx8sQir@ktmo#SP^u4aDj1b( zu6Z;Stw8(`#J5_*ydF3FiPcw-zj_2+DAiAATd-2pN4xb2pHis1zHe26Cm?WFw*kmsin+o|ZQfjtS zl*PX3k{x1OyfhzH7B8Z-|G$FFMzWMhd<`dveFCE2?=MzWLtibk@KMa`A=3})NQv)+ zdPUcUOP*`}>hHv=f7 zS++*K(GGyRGodPn(nCD15mx#v2oKB@`BSBQ1rLmEdH7J@mW!HC>dCadWI*B#oxEjq z!E^7_b4H)WxsdW#L%9txixA!F#1%1bkuJRBUn}HBqwE!DSr%*jwja5$-jE-oO7(M! zve@G;*&(*wOOtpK$J_rOyG?w##Is3E;}zO*5Tok3LRB&OK$3M(q40cMYBG191DBo zEv`$&8eOt1*2hb;dDEPgH0OBb^ya2E%D4mXMU=V$EAC4w2mAbeS$@&MXb{AdDmKF< zJH%!~ez4hyWIxs!Rm6f(dXv__52Y`~fiKuhM^PlZ72Z~C z1C)(Ni!B-51F0Z!S!JC2;732f3=g3ayVoT<#MXJK*d8wp)eUR8koOp-3^!Z&J`R5P zzMT~ZpE>K`KW81v3jA_HFmbN+5e1(_AN-h8b{r=Mx^gdt)EMHcT=wx|YhtlnJrSqH zRcCwKwh$G+$590FI)b)v3Gl^*!^BAwgt)#o8T+;R0*DB$QQiG;+<8*f`@dBclOY3 zJ+vDS?bbuOLzG>3cSn~ZNxJruk!i}vOB;H=Q zR}MGjaBr}#EW`V=kB@PaGV^x=_%!(W4Rsecz=NIM6(^g3^(w+fgJ*&jNANQ%){khY zTUuOMNb>pb+@*eQ#7@5+g?##=UJ0Dfw*k40dt+=D;@JuALq3uI_JK9j+Zq~|)-J1A zt{8$&-%6Z^uC<}((wb$`r1>_XyB&PK(SAWqU&HQ#F6#Cwx_;Rk-c`MBM=rl%4Cz$? ziX&h>X+OwdJy?DMxCH61_a#2g5h0e*4W*0g8+zKh>hW&tLr-I$N8S*d9zs47Ow2dg zc}LF(>!VC1NPiQ`EMVbDS#1raNx8B%W21f!@#_i6GB}i_rx=HUN9Ouh4=uKse0sXj z&Xs!7u-53efbAUqcn)s`Yy5^dwu2Qz=)>LM-eBrHs=gsob0xNV$A!uc$CzGbu?pO3 z#}qjD(%VJUCrP`I4&`n{XX~*whj-?%o`SUX)MJo}TVq0h2f;p;{x{VxJ!)Cq@*X#L zU(xNB+FNUGvwf$R?&!D=ap=)Wdv4NWl(v1=flJtja<+lnz#|$OZ>se({>SQ{`N8&o zjoT{N>AAu&-Y%RoCf7gn)DO1j+O&+FZ3jJUr*RV6a0R&J{J1grk!L6<#J(YCtH;xh zWUI%??D*?5K3lGTF>YG!0Q3XWa>v+mC!({ymVnh4Auhf2*Vai-YiZ1cakCBV+t8iQ zFRy>bw~Bn~uXchz7mjG?b5r;9jPt4bXMRWn`j!sutVef7lgIQ2?HsJqnDq3F9p`#{ z#*fYPoF3xSOUQiPGvz18yI!Gop)0db=v$`IMt7{J4E^anZi+F?ccDKQB5(0>%_$Sq zu0gj2T`ASMz7D$G=uDeURFj4F?mIEtA8p`g@e}k-ys1p-5S!kDH_(@pSx@N=c(&gr zgX^#hZ9NS<*X7H!%cnxOg!1${HT7kPUGGy z|Euppu6GIgKK1i;@S~Rs>KG66c!{+B^g2V;r?sJUQ{B=Y%epV`c605Dnp^CAp~tB- zzCzvf3{|bcOLBN=jKf%30bYZBZY(Et(YqSe<{`FFj$Rq5b`MzZjI@0-HO8Th=Ybbu zAD=(w*zutkxb`x>^afY?Dna_wYhV9fJ1oIQWtEhHKfS;B5ZoMh#VP){OJZI%IUBQH zQLS=A%$vabvObb_S^aT-{d2nPn!j;M)<+54mh%z(>t*PtIbFIo5ACT}uxm_&F|rms zri=1kMlOG$KlRRgKi_1If#ev`_cA6qKc#f$x}u?})g2@ak&X-f>DwK<;3FIQEbYFm z+w$6*YgRDsiWfYyLzA3BOWJASmC0P9IpYF57q3O9_(DDOjG8asw_9inJ<{geGo9~3 zTj+Z$C42^{>oF?l?>+?@Mp|o zrJ-jlhSaizTz`5nt_+XYw->u}Lfg?}G>${t>)E$+uu0YvBN|GVH}p)}Uf%^8>-6S2 zrL|#DM{bR)FAQl75ZXp>%JY7cb9$oFyYhT{rtKE;Ek&nveC&?2^P1lJxWVbte4%~y z3P`&KxiE*zIXoeUC*|;!IXpdwXXWs`9A1>eOLBO54&R=`cjoY#9KJV)*XQs9IlL){ zAI;&dIs9Y}@5te2a(Gt`|2&8H=I~27yg!Fu%i%*g{ALcC8sJSKIlMiGpU&ZDbNKlj-jl<>&f%AH_|+Ufn8R=6aCCKce0I;_ zo;lnrhx_F42|3)H!>8r&pd3Ckhll5IYYvaj;juZ~k;9kg@RS^$mcui1cy10ajBz+` zT?c-Eb3t47T%Vp}W+dl#onJ4oYd1akto2Pe=jd_iVMjQx=y_mktEY&MWUI$yt*sue zJ(8^+FCA)P&;zNen>p_%=itVftj`L`d3=^TkGCFOyhxSeFSLoi%h8NoeC{kR)yb2; zlK$gQkC<+OqgR`JI`%VJZb~xXPY;v+JsUkX`uA+~Sm;9h#bu3+%lfFw3Tqub>Zx@= zYgohR29ICExV26=x3|pk$4;oHzP5N}>igr)_xVRBYb|}jQEP#)F4QL+N5$osb>YbR zur4HTh)G|Zob7zO_3iS)?0UC!Y(08}<9*QSPha?30;j*i^=i@&`mA3|tfQSr*EiI; z>)4Q2-`ZT9%Ny#t4xO$mLhO3Lb%L`^u8Xhay13Qf_8_W;#-!eQTT^EB#<0q_Lw}Um#l^yW zf9StM*iH2Lwe}uUuW^b&(3d~cwLY|;o@?Cb@~3Od5WAi_wAW?R!1rSt^gTbDJ?`|4 z+_*2oy{BYe&a${psVq6I{ES z>zCr4tf0dc%!s2$HZ&zM>eES$8rLwo7l%4Kj{?w(=L>&Zv8H^-U1 z_mlY7qmVO=e|>&iWryuWKtc4)#0ImyG>>u*S=g?r9FX&fl-F zzpsS8o{27hzcc7=MW=R1u3bon2qVaFOwX9Nt<<`)A1GJpAOdVLz)}Sob|b6&SFLh z$K&jq{Jo)YJcjbtAYbF!C%#5p*w8cdp&l?D>U8d0a7ma$L*A+AkJRorqSG~MSHBq@ zMkgPk{hF5fxub+ke|na*1D^C5lg0S>zJ92Wo-fsS3-t-U_QvvH*SI{))&9CX)NKj! z739w!55Z5}&Dr^_KUgsbzb)W?ru@(*dK}bW3#ao=$fIXNZOkFhKIDGvr+prD{a5(5 zu;V_|VKF+@;rwj>Cv#KnExujtZ=|7qW6%vCZwb<$o|)VXf4h5f18()>Pj%2=3;0QR z7kP^9_0Rms-BIz=b~65pxB1_Pa$`0b3vK8d@fqUm0M7)6GPi-J^FvElWgg0v8GQ6# z>EjMqPb{hpL%-<3#^K=b8<6G;{jEkmC3_qf|GA5E;lVFv&&SD{v8=C2y7H7?tsJof- zUH{j$HVjyL^s@TpN8MbvqQ@=WZ|!zl?aG=F4Wo-+sc#t2`po}++7EUXak;kI{|}r8 zoey(w?1F|Bg~|TrSeVP(zRZ1HH)i@~?}(QhRut*EzQh%5hku20ud~g}`xiAVDHg}X z=dIAEqt~)E$(5hXEu$L_6^bL`A5KbfUG;qF6w=9@JGx;B=WX}HZ5VrcGIgP|adT-~ z!@^IvWowA*F!~Z@wKe4Ga7n`|)mL>0arIrt!yEV-ovw{l;??2zoZ;y7cM!W0ea1Y0 zq(1meS6vYLyy>4f9{hZfKNibGMa#OwF=^Xj_c1qgZrf4x*tK!fCbk{-^D-Z2o2&0= zJhN(6g~Plxd_y=63Y}anKef7d*KHo<5LfiZO3+G+7HF6%^VlX*0Vx8u`}zR;=RT$l&uGZ z{Pjb&?CX;s5nGQAwtXJ-$5-xLP-NDLZ^pMC9m>}eAQ~^`+IPCCdNP0Nd4Y}C#q05F z!kQ&1b2I1DF52cI6JIEE<|F(zk*o9kxXxS-CjFv)#yTEN+Yp<(6qH#Ko3@~KxH@Fo zCedq4=)^AcVP4Y~&0$WT-4~&)nkD4nINbq$Hnl5Gau-kMA06~-9^;sBZQ%SAZ;0;! z1pU^>+3|1k&yMwBTyFVJ_P3xNV4Y*^eyb4kq^;q18^8VvKGq|bO|aVnE`h^7n;_qf zTyb&yyS*TNNbBz)a;-hPTHEycuHR1*`n(L@>uk*&T)fivctg~tJ_>OSdLnxd?#vt> zp2Mv%4sEABuB%;HUH7rJiJCs-(wYSlqhtVi^w-3O(e3cohAz%gZ4JXd?@v)lyt}ct z?YJMT@f+GJ)T?n@b}ktNewH!N+R)!#J20R4>+q0IdrRx^7uu2qU)?6>KU2TrSTnZ- zTkZGU;mXL(j~6wxaKl5VgQOlywrAUYc@EzmuS`=yP&Q;iT@(Kk~;@3;Fcd0&eWC@nY{gOgHx( zxQWPFnayg&+cMI0Oq72T&as0!ud6saYfTl7k9j{0*SBsynBeAT^d%Qf6m3Gkm-Dsm z*~WW5YtAe9RYB;FRlC^R?at}_^XK%MT5Q!Jp&S$3Q>g4HJ>)_GvRW-I06h-;H$E$sH@uO?#P^0#|Lzy#s{N@6v7UYNfr@go~G zQLV!>|5zv~Q4@6&Oa5viI?(*pL_G|Wy(>16@inj{twc>!pTn9F)4!UeMZBygIwn<; zs)^h!x6 zK@*K$IJCFEPNe&#;V;CuKkg6dwSoH&V9ljP{wY5jFq8OIp7w3S`1AS}gQXvBu#3lF z>9zk_xkLWx21`HF;Mw3$gBzW7F}e|X?rb|`MI zQK??`+P5xi+24o#>EITJy!~{em%aA0!}u#ko3XzJ+~JV7-)i)-*M4_d%YGa7-v{gd zW~l#~kED9pYyZ15$3p9jAA!U1`ZMq!zyqAOV)T3PyGQ%|@bXt+%KaiZ*dGUe7~CG) z>v%Z;4*pf1_QePMKJ2@JMJ}vgA_%<4Q<=3A1kiUxkgWzyHKMZ~ZECZGQ6!^Q~mKe+5A(LPJ zw3i;nUomvS~#kb4YE5G*U!}u#kr(yp~usvS(8@=qcXWyA)e+Kq{0uOh&i_u8%>G2-^ z&_AQW!Cv{bmp|YN_7$+M`+|K7`|yKa_S(}Q@J#GK2G;sd_BVq+0}l23D)=kl)>yCl z?l$@5Py75~{Be9>|4Z;fhkX0CjZO8k*WQ0w%l;?Wj|YeHUOB zw&x0?m%W}9aOT**g#8!5cK%;y^s?8p1i}7I?C%5Hc(xn8>~|Rau3C<7u-3kzy$>3_ z?DZ@{@P8`ye+BCrQT87Jzl-MwZ2OJ@2Ycn$a|r>rV}BXgmbcXCWxvAUIoRItXea$E8?$J;M?7UnAbzz*_l-@wwXQWv^#Cg1!2CtHILi znGUOuI#MjXp5?ImF$PPoXE=iXyTnrg+wnEY=%35!U(De{IV}HrCM5V*{7Vg%eucr( zuQyow4F*fU(_rcK+z5<6-=F&pmR`?}ICZRV8lPh6^(=|iHybRyo-MKZvcb~pc@nE% zY_RltzQpQR87#e?H?jJ421~E!Ppp2c!P4t_6szZ=EWy(2c@(RkVzBgj2F2<(7%aV> zJ+bHJriQ}n+%p-&xKh1E`z1lvmsXBI5EZ2>-i9?Z!uVUJuhPQjhCfZ`X+-l ze)<|Ly`Cqr`L`P^y`BfL`hx~bujfOozR6(e^^Azsw;3$Go)fY9B?e2c=S8f3lflyK z`4Fog@X-`YuV+B4exAY7>zNR%?=vaI((4%ys~=#n^m^9A>c)8;i-)FG&dOpPJ7k@0p((Bm|t6y!f^m-P=>ZeRjvGjTl#Ol`?EWMr!vHFb$ zORwidtbXRlQ!Krn6|wq*220;KC9Cgiu=FK^HNGbrEWMs1f$`_($2A5^uV+V`I@a$o zSo)U@mjB^br1sM5nGu_RwZYQsSrMymyE4Vn>-i9?Uv04TdPcQ@*ny`CSj`V9t4ujfguezU>S>-iF^Z<(56>Gk}G)gLxkdObU0^&JLFujfXr zexJe8>v<5X?>jBU((4%zt6yxe^m-1&>em=7y`BZJ`r)5UvGjT##Oiy_NU41#O6{fBK4yu-@z-Lo^xDhp)ba5*)?n$|4VM4K21~EK&o=*7gQeeQu=4LWSbFV; zw)q>cNwM_W>umKyXQfzr?RB>L{RT^~ea=?D{?jRzUVEIae$wm|ORxRSR=?9=>9x1n z>W9urvGm&0Z1t-QmR@_9t$x43(rcfx)lZq5V(GO%+3NQiEWP$9TYcYaQ!KsqC|mtn zgQeF#Wvkc6bOM%MdyxZH|27&dz4j(s{cwY&*Is3-Z=0WD>9t4M>K7U;z4k0y{WgQ8 z*B)l8=Yu~9mR@_At$vrm(rXX1)wg{n#nNjJv(>LPSbFVYw))`Yt`XslD{t-)!@@87#f_J6ruUgQcHgu>3#!xzt{I?QypG z4;d`I_B>mCX>p3B*PdsqpJ}l4+VgDn+YOdpd!DU+(C1Suz4ky`{Y-pAJ;_!dElIKTeGHcW{sv1wz+mZD-;m9})nMuCZp`YnR~a#X zetkCDVENa6Wv7nyQw^40`+)$1cT0ZTvC z;4gE2z6q>7%i;XK-{@tpeaj)g+M{tJ<(QQGFW=;k+%Bb z21~C!(?S13>hnpk_DYBLUT^fW*M8|>ulh8vNU$p4B%U=6&gT3O}`wuC0{tWJc3>+d2gzh zz4qV+`&Lq?fTth`_KS^P_S%md>_19s8QA_Fbin9kul>2f{%TTx1D8xZb?Z{S?6pTX z*e@aVUhr-NA)dWPFMI9P4fg7v+J8#1^xBIX^mmgw4y?Vo!C#;IQoZc87dP17N9t^_ z_T~orw)<1P?6n^^*gs5aJXrg4gZ+RFsb2QllN;=}kvbQ=8bPq%ZuGL(e%xTM`t3JZ zdhO2*`k#>c6WI3mh6hr;?6prf*uOyP$6)Q-4dr)iO!czYUfp27pVS<%_U;CIecd7G zWv@NE0sn>68Q`79e$Tg3z3jDjH`v#*MW_JoW9)lAnCfM({ky^bIPBjIUTo|aK9uTZ zuRXlMz8U*3f$eyj`f#e3z4r13`*X3s2D}kLsBh!uRPW+3_#(vv)?VLW-|dNkF`zxMeC`#IP@2DZol)^DeJ*=xUVu-ABL+LB`Fwcpq3XBsTM_WN4> zdV{6czF(`~Zm{&)2W<7XKbB(YwGY_pH$0wV>9r5o>eqiK#nNjZu+?uj*p+Xv>ff<7 zwU=IdgKhrR21~C!!dAb_VCl6t*y;}$EWP#!Tm9(ordWFI3%2^^CsHiE_5xe|c7vtY zUS6wj{9cNs*Ir($-(j%y+PiD@FB>eq_VQYN&uuA|UVD12et^N!YmaZx-^_R&4A!3C za6BCReyW$f_WA~U_18v&rPqF5tDm_&#nNjJuhnn-L5ii9sf3>W3RFz4ppleTTu)H$0uy@BDF!rSJ1h ziq*b@ev)G8wT~9YpTC|O@Y57auf4NQ9qTt3EWP&5TK#r|rQc()%75UwtbLz;qXe{gq-rPuyit1lTWz4q5y{m@@z@yr~?w&d+= z+ho=I-$VZQ{A0oY z0sddp{eCU!_k&;L{@x_~2zRTGqWPap^}WG|*xy?~e=PBx3Vv6s_b2;v!Iv3-W$=G| z!e5_Ce;Ih)!L6TTmO>I#4TDSwZEzjB9v zworHncE#-{yFfMA7lNAKk0t~9&Gf#2Y39&x2NL!3wXev)A}9LGm8Gr z)aU)+!<7(kZ8RAC82zRFNAiCb_^1W`-3P)S0f+I{$o}Yd@S5MJ{W%%@OU^ID;{2ah z{&}o7VtgZsy|i};toZH#ZzdkqSN*dVe2t0kVeoIw_*5&*FWX=o2ot^0>5a=8ws}ke<}DI6a4-b*-r!aY>!GUalD@fAH#SF z{dF7oJ`?Xhfrs@9^{a_CfLAj<2+NI!tza6b)X0F-`1mDwEA6TKD{7xtzy}Wa-*1GA zY+QMa^+5@};(4bwvZ!$W)E_5<4=}z%d!7LvWcq&;_|K-kmx15O@vHGJf7gKDeWTxB zD10N>w#S{|Q*ZYB<)r^Q_!u+(z6YLkTvVEfKh@_M@I|IQehKc+{G|Q9s?P!NX6!@# z`GjjU$sGU3fj?#X?-cMh;t%y323~0JC1CCilpt6Bd~n5-e;fE?v|pHS{uz9b{Fq~3 z8$AsE?w!89Rlmo;i{J9|X(RaiVA~$gf;X4a}9UvNb_ zzkdb%mmm6Pc%*+2`~}uSOh;}!jN>y~V;JwNI4`Juoo*u_? z{(Uu#rwRPoTl_QoDz5}CT%L}nLEv$w{$s%-nLl(rC;Lg@myG{wz{k;^I-aCo3jS}C z|101f96x@%P(Sc5OnW{JzHDVW-gkp{n&XYOj$WZW9UrQHZLjQjI2t_0)W07%oWC2f z9{?V`kFQL){KaSl_|qo-c5s39srp~>PX_mVDLvoK1^20z8Y%2Or*8keCDf{ct zU(0;LX^r@5qA!A9HvS(4@A+tyod2E#e~a-jD$f5j_+ryuzXAWz;6H;mGryLc|6+9X zJGuXi|8V@h3w*OFuOIkogUS@5wlIp4(UO^$GLGx!_ZNl%8M8;43=({#Mt&;63x{4>D*xO#_dg zl&&9^fTx@F*_VV(dwv6awHd!pfR8r)^;7V8=5Ls5kC#Z?W$fPsf7jq%$5DTd&!yOF z{`mm-eP%vC2fSxNR4O@rZ8REuEa$u6e;oJ*`b*dk&@v)+9Vc!%kq z{@{P$_qz!csCYj(i23NsbiV%&PE4O%=-;a({b1~Wz<6ql>o*EK9zBPJ%YP~O(3zn= zwb9k!r-{$c7scoX@U^D@R)eqO{O9XijJ^i8=aWak2N>T>x74R5+78}7Bq~jh*9W`6 zKVrSPIxhbu@b-75`nSM8FyrB!?~0c2CPt2kPYZZA=a0}I6Tts& zj`vT4|8bgs*R1j{0Y7HqT`hcRI)B~=K2Vpgr@jN8Z^rZY!A*>xW)~m*34Zf)>G|?f z2G&B>v!yux?&mNcF~8~=XqEq4?DzjRJ>M0%PP^^8^msf5yv(%Ud%zJmjHiLZi_`hz ze}M;+U*kdkM}fEeBAtJ(1aC9*!8~v`=I1f7{ZjD!#c6-80e761*8d@Jx*n{Fwt@fI zwBK&MP|--?*tF0{d|3DqLaXvT$Y{>&IX_LM7kau3;x;G zbo^ZbzR`@QMPMD@G_N~eZv+1ceUrmA(O1FW;QCI-yW;t0@R!D?dn0zeW4M8vE}9ey_P68vx#C{0#$-rv5(OnrIaGr}RfD z&OZtK8`HkCz<<2TzkgZfeI7i_td~|P|AsXGSHU-YEImFR125)$6voeU;PH(2=D57Q z;4Lqw+A4G*QvZ?-%I}gO6R8&!8^=+KLp&voUcZKx6uA)<6ra3$HBvx-@@^E zJ-C+f8}3hh5!|gQD%~5~-wU3}{8@6i7;OT7(6q;s;M+~SzXqSa#J|T)@g4+keLWrj zJ-D#H$F%=Dz~T99tyhl&AGa)Bf1L{ctf@~6cpd!})?XKb3mh+@{T}9gJaVFckB7>? z4E^nt=i8f4t%FbbnSVch~+zbG(K2dj-7K%(s67|ErlFk9{B4Gt4h^yE~us1D|*e^^51LGr{ZVPhB6W zKg-~oOn+Pf-u~;fy=Q_qnRph0A2aL8TfnP~{;S{$>-)XYzW5eD9)-7qzhufA z$nkd~^Toi}eh>Pip7ig!ko}9`;lE4A+hOn$Q(k?E`)3?aW8?gd;GU*EPXOO&><5AW zWllODoe3WCS&sk6$NORMY-4{J_^+mYuLduneSLowqfdk1y(OJ*7lQv{@-GL^VgBqN zdH-LQy_v7R0Y1R`bvSyR&%Y1;(#O+yc7p%$`V{X5FWi&fzk40r{7QPf_c)Q?r_gIW z%isIJFEd{Rd^-5`_H_M!4tNLghVeH6{0`=ux#U;-e**l-mFe~NOz;TPesjT>nB!|1 z_{-!!6zBg3a3lQ@QGnK8_k$Ok_IVJzq+5FZ@*H>=H1jp-v<5{&JSTcJOgev^W!hTJ-MGUIT{}!XG!Rwg+!~DGr`~}l~=W=`vWPTXw%CCw3 z5&ff-SB&F%7(DXxzJFA|t>Dj_R|9;?~ zaXxE{^PdIYZ0-k*1aCF#pK;*-GVy-`yq@z}Z5;1H@F$G^X7KrDysiNcUh3y_#k&#w z5mTS1z_ZMFcmcf3;8(zNwxsuq-vEa{-OoJg6xQSPKW7=&|4raKxV{^Pk;)qa*84M5 zf8C#Jb@qkQ!4LZJatU}O_j}u7`x)T!FS*}R@W;n8@K7^fe-XUg#J36Dm-Y?w%a6e` z7!RSn{}bGSeYpSf8hAUnmhv=SYx>bYX1u)%{A1=1oj(=-`@uJu`whdv{W*Sv|1seG z-0ve?{BeB(-uByczPcXVV%Cpe0QXqs-{UC%_kyo5_1O$Q!}QOOz{h>hzq?8HyTP9~ z=i@(sCmH|$4gQqz*Yo}SeqxT7_k!QU_*MN~|9~Gf<7WhTtr=gFz#YslVZQnd_+bBZ zy}c6LX4>!T;0ss}ua5n11Mf2B{{sBT&FS&=U*Mik7fL19eznn?;58=xCMJ@Rv-~^1 zU4MZ4of+D@HW~{4xM{C3;F{a~^VqWgD0qV@{~GXDuTAd{d>%Z&lz$8O<$Y=YeHHvk zb3APUpJnh4@GjbCw2PnLv%rs2o*y5z(ci#dH1QpCD#r`)hw;}Ryzk|-yb<8aXVUX+ zJNRLf|0?hk`1j+jHu?CR{j;cz@-w&cBV|FLJ!FOmpMuUEp7u@;?avox!c(&#&XmcOL)`wzg~?o8M3KLgL7%k^Sd-~9=^ns~zaIs`6U zkzOzK(21V)(?Xc~uLS-X`zKn!@^?CTJ?G~WV|@#F0QYY?V%!0Kuj$XJ;N#5s@N?k9 zZxu?IGvEK|tvkS5O#eLy-U^n1TMvR8f0vH`UEp7FeG~fg&)@;(c&|B~^@+K@IR^Zr zE$Q`4A22URNb3K7@E=*vhxQ!^w)gW|z&n}mLj5L!PhFW_-^>=?k;Zc!cs}Pd?dMc` z-30#GW$Ak7F7SU&@b!^?J@{zO_ZL!M;YYwXoALBx@EyDSd&i{T1Kwojt5?Ctvi>i} z`Fn67v4{2x=bw|or?P$v^Z8le<`1U7*R_F<`D}Xr9}oWWxb*jotHJBdc)1pQnD$e7 zIzQe7uKjCzKlCo}w@v)_gKc|01pc0xpSFS5n)^%V3}rpS{oTHCef~H4&8EK3gJ-^& zuHSwKUeE6fB^OUIIt2cxnJ*exM~t}Dzb{(tcM`a#>Hk6Czb;7U(^l|et|#Wj{w@Wt zHOKE2;Gt$cJs&)s>+>)^ZU&!k?)R((?`1s@)*oL7?>GLogZpwkuZ_#w1+K6kNBdJ1 z?_Th++tT^<74R3BpX#xfUN5L_Iz8>rcYvGC`r|b4XBl5DvHdV`|HgFvc`^6~(_fRp zCo+FD#`d2AZ#DDH67X&l|2_Ywv8w@=q^iz$*~QXG4B{pQ{w~QHqmjq%TR_O~&c2zq zZ;JQF%)DKeAeWw*o;U6GOwV@r?AxbAF%lycl=xSPu_(3tDSt3k3RcvD1d^y2Awr^2 ztf-3>B*ttiYEg+Lrt+P0?!A4x-vhH%%g*WFd(S=RJKs6?%mDe{fOmo?JJm-Zk39X~ z0~tLeu7BdIkZ<$u{|@BoLq)ysCy+1MUtIsJ{Yw3XXU{K#jGBnU>jva2fbX+rysv_M z4EBz&%KAA8x#RJlTOgmo{LVAy-wye1&mKPn`F#8@N9=y~7a*VX#`{l@FYxB~9mtE1 z751_N19{(dh5mL!ei-J-ehU2g4I^Iw`Cj0gjrSKpKG*ZNgOI=Jy?+JrYd!gukRL#PwZ}Z~ z1mxr1`rZur&4_3A{BH2U2R(dv7tX&Q^*367;@h8s{8n#0z6?3^;?uVwUj%&EZQl1d zWn`}q00)Jq`$)Z?F*L%th$nw#g%LB0U`C;umZU4-2C z>iYx89oV<{&spje~5A( zc$1swZ$iG{#$r5ghJ3xZo*#vL_h)6liq`WE$ZzxX^AO}Gy!k!?`71vx)&sFtT?9UO zo*B?)iLcR$4yocw%i}t?MGybS}Ke&mC{y_3`nlEw@bqnl8@{zODHz0o<{<_E9|9!~& zy?FT}$TxWQ_e=m6?T6rU-fuh)@*da=*)#Ev7eao>)8{_OsVCPU$DVu?@~jshW5~B) zz32sG|F4A{q8?)H<4ur12!8w(oTvG`9rEE5^8a`u`Mr=|=RN;pkpBbm5@C#=_Zi5m z;1kv!{~q!Q@A|JoezE62--S&5r82*>)DIwk8rNI={|RLLas5%}VPoPZ?|Hup`R7qD zq04A|ham4qyd(c6c@^?w-t{TumwNVp6XcJ4x>(P*Lw@}mi+t=ekRSK1e-QHh9{!$& zd_CgrLA;Of_xq65LG^U}7xE7EKad}Vz0@Jocy=Lhd}KquUqSLOKz`=ez+a7hJ|FV4 zz4u)V`BO=e?;nIbx3K;=!emYp{PCPZuG7+>6iGK@P#6i2sqjj3Mvw;@2A?KOOkEi{FR-1;__I zd;bLFJMlij3)BMv2o;2IR+YE#loxkl*U*`#q3< z+lv=>LjEB5{`1XvJ_q?t-g#<9{1M=B77wh|e$h%R` zw%30W@+ZCZyASe9!3VE3^ZPpFYrj{-zyE}MiHCpx3;AXb|DSsh`1JXrUiUJ{?|4ZO zKjt8R&9m3vgB;`eRzCyCO>ccqLH;$*9&e`W&)|AYko5T;$Uo>5`PauFhpBq{jY{n0 zPRRFSy{tWc5%RMUUnb`K8OUeD{;xN(s$spo_n!;-^|&8q&GvU8Y<5@K;TOaX+}Yn*?e9;-veki?cjP^XWwM@tH*J&DCO(Oy-iw zWr&-9em%SW1Xy1kj@;iwg6VXnqW zGR-H`T+O8ove>apZS(SlJ;fF=6S^ds{=l=Wq5ovJ|1Lxl&y+UqqlW{f^Wodv{I=kHFDSXZ? zmbIpBEreesOX6s5$l&2SX zhN(T@gDGTjl4m*`P4Z3u@MM||OL8*tMXVYYbd>2d1W1K?nsx2zDHaGr1u~@5Nv=hy z?v;ENcc;;K-QLlQ!ywgsraK)B>|w#3z}@PmIGM)r)D6cS4r07>9OQ2L?u^0Vd~@<- z=j=Nx3sHi;?ao!*zjOn=zZ>P(QqZ9b;;?TI_LHo7XgW@+hq`gl zJ6=5~&kNa0G%ahM2IH(3Mkhn3KYILAUqNqs>MP{sKjRwmWb4+}6~w^|{KnyW(A(T% z&t&>?9fv^+2KEt7=Ck$%@K*cLUT9?p%JzVP$BJ0ss68RxX{E}#Z*v%qXpb7OaU!FO zl^7+gV4&BE=0-b7fh8+plJ+A%!wPPO_JEmTMJ#4$Psj|dWQJnJ%7nK$j0xMLlQijt zSw==@?f9vTM0FbN#hRX9tR2>iE6azQowa&%xwfe1R+c-hl||L+%;{Pjq$2_>gICG} zvI9Plp~14#WN5QjA|@h!b<^ND07hftC)IR3&ES7P!M^TI(=;6Cs?%J$T+hwhO}D!^Jcsz1Uo8 zcG|kVy3*>XEEt3cICho$DS4!hqfulyo@%chs`vF0lCgz)RUmOcoa94wWM%Qn_R$(% zr&q7S3s>f@)SavKQu}aQaXbJf!NxYgs-ZzXbYdD#!_3SV>n_|>t<_qc%Y6+>MCc{s zJWXN?{A#YYyr16S#UC}lwA!4V+pil({QC)h*#FzB%}e|f%<(mw6=~Wm%u!uy&xy{} zMiAw+;CZ6ENdnapF!z&%RxpCn;Io{)*L6z@YdW2NM{nv7<(-rt3|3 zNUdIPkpd4?73ev{rM{+~Dh^~S#cMY8D2NBsaW9XOF@8Wd?ay=GVjYME#6%(*Bjdm= z*#sOY1mU8kiNKj&9b#?MJ}e~E2;A`|x#xbvGfJabYacqLvYcW)TrufSd%5n1SE9s?ZjB!wWY-5;e0yjx@7Mw(= zg$YdAPo4F&>xY*VAw@_ykr77tctOEAN;N+NjWeSOsNqJC_lEssT_2Bn#}%@}4*qqn zMyiXG@p_a`;XB~v)=_x{TUYP*SIY8TGvU=Eb^k260+c*vWKEEyu2(pCp(+F3VuS5^ zbH#rSlMbClF+fk8LRp#wAkyKfNtj0AxEGo{Q_@9T@+9lhsV%JnH0Zw`Jkvq|;dm-~ zgf$RSIY`4$kq;R4s@4jSRbe>nB&*@L-#+5BSA;I3lI5kQJE<|$sDBFV1SxvdOOq@F z&h&L2jj+N{yaKFduxOnFrO9Pxbw%M6DmZvpOJ~B#w%I4pR0JHfuEc4vk}%u#^)Q7~ zT3%sji)h*>vH+EA5`lVWk?xNIB|wO=P7;{+tlmh$_Qa1{SMMXuxrq(oL3uX}a!hR< zxmO?h%SL2oKwPtWco0Fc9Y%OklTx#29X6(n+GJ2R+K_eTG!@YggDQu-PU|ot?UE)W zDnYD^ZG5e_XyOwF(AZg-*7Ji^eZE}>q|GmOO1AABR(ws|R?u&4mEX-ua0@YkJ-|f+ z@-gLZ=IC25yj?;!F5`$wd5+Q{FSpUtMg+C#Ziwi}69?^wu~C`F+(Syj>aIt?q)iJ# zWbBLr2oR3SC`$soh_3nwE>WK4$WaiDPe#2^b1FE9Pz9-wCJ$Gpz{x%a+RI@}HlpuG zL?4Ey`crIi@X8p)lq?i6@_+{Jd9GG@jV!yV{ScHX0|zuK@CD=6lqSj?ea2|~jJ%gE z6bbKOfT_3;yV|IOnUM;V10$-*XqX;i0pf>StYW1X>P#8{P$&4q1cfz025V!C2|n&K z>=r2z4TzVw7;G@oLr^#uAB=;SeaR3#PP>dRt5G=WO*R#|ClCtGsr$(|R1pzY3JrxV z74Jt%rjXG3k-Xbas4@s=NN6Ex>YbHiE1jBN#NVab)w;HPc(GyBEsLLtJ@VuTd`dQt zrJb$NpK6R@PxL}-iRVHA`+;z!XZlQ%?bh9lZB%T)%GDVTC%K-?`b zDbv*z)N-@kt}Q#4G@%`N?fMkhC9z#B97~lYBOD=QMY$y#0;eh1OzgD`XGf-c6)v2y_B%oD-EVIL#?Js<-80~B3P*t@Qwij4x$N1WIzY4F|naa zeT-oKGF`8glFro{rErJV+Inso$)Zr@*#no0D;4oyqnegsyOyd(4vK{r`l)9z6GAB{ z1w36-*-LS;lcKCx8dR}Vy9TeWt{xEseW-<;u(@=F@0LR;jyeWlIaUdQ7%8S1D9x-m zxW6k=*e=Q$!wpsNuL3Cur^ygd7DEh(vc%6b|r?H1Re%W!glLMs)Li0Rr8Pzqw$iNgR)?COnB z%i--IMOhbUnI7S0;H}st9KlEV0PDc<8*$rnD&VWss;Use$^K=&`OD^B(&GYB-(n08 zQ0hHu(i`bsI*l^pW2zS?ph{UXO+ggNawz;IeH%oJ_|{l$69g#ZJ*Eg+Mj-!9q6q|D zWTc)Vtkx_DcySAkF<&nwAeMV~k*KDd8vCaN7#Y-S!nk90hK5&x!V+0g=LxkDq0H9R zv-`wGe8(4($~rAs`jgJk)XoTt$`u!$Qn_X|1lK2k(;*Rk>pTR$NCw&7IZ2tNB&GCr zfobC5OyLiaEmGbFoed{9l{ZUi*L zQ_7TfCFsd!U1>OU)g@}OF%qjV^>$c1oV0Wy%&wFg6RB%TDuYciRK&arvw9U0I9qiz z{DX)oxjPY0$(tQsaFN&gr{DvdC@x3ig5=av)peXqBA&fRs^>l!R4=zzN_e-DB)z~- zyvZVz^I7&qk4$#Kn_O& z3C1%iNJ*Mh&;nvIbLIJxt5q!8%ZN${jSz%7yLcP#EDP-TGw1Z(kZxJ{AncN#Wyhll z3IkLfWgq5Ds2UVR_f=Cg7K;6WGKW}bwHNDZrE^%%tVNS(z8^l%*o9w35IMQgf z8+Bc8HSx((a~oGGGuKGX{K{_$i~Ow#O5utzaVkbwQ}Qv}Jp*Ilv_~=)Vt3l+)ZE#! zo!RhSk{3(ZJM4Cn7YI0ybrC`|Z2=rQ0Xu=`mqLV{x#$*~6|@V~DsLOnR7ymdoCaLx zWW?pj-f%iTE)^cbswJe^SYn(48(!3xARYmHv}HX&%`8KYw@t6O%)Jl>6RC2$g!_cC zsqMFCGwclsi?Q!^t}zNG%+t2N(dJ6qQDm`Hc&Q!mWF1yVwOFubv)Lt-txDv}QQYKg zqG&=%n+s*&OsJ%DP9{`i5Ja&lWtU8UWq!Wh=pfyvF02&gVT=NfWg;bq7sqye?p&Lj zLw(A0`%I8nV)tuQ9j%ABhMW{Maui^b1B>nz88cMbZTHtC$g+HxCe!sH?<(!TRPQeb zJ5CfSw8=sbiBZ_MsWI<)Qg%huQq}6E+_P-mG(%U~ z@Vp6|ggkj0Ni4&n_dXd4Rltz7Q`w6NQ6%SmLYGCzn)03<1z1YkQO7w>agkbl^#03~ zP~K4(pfoO>Pd&Iz)MEpo%}t>|v>2objylf6am0tKSNLJfES*IkBT|-69G=R7)YL+Z z1TGo|D6UHhjfWywx7@5ZOz<)ZLBTsq%Z&(HqFJ7A&~EVLT!g%$84 zcolMxu+$Ji$;Jk@GT`~lD5EjuraeLS%UR z_*6|nZwcxyMdm663^GW-z$nP5DVpn$$TX1rp=-|v;1VbjTLlsgr9aW`*TKr8v1E$H z?hIWj!G!U>wz!D;-s)P%kl58)yAAtVSv$PoYES;!!P*Pwk-{3+T~NdgsNI1!2eG)a zBS71Iip*XT3ooA{u$;Pz;189u6<nn-|jRQ7YV&6wX+dnxY&MXr4-4I1I6xQ7Q$Xh|7ce6LRq;CCfi16t}Sl#6RrvbIcg5$7S>eU zR*FH~Gm4@+Cobs(YX#m=_%kgLlskDZDw-V4wEW0n4UnG2{ys=0>0PRupmz13WCm>#)h3;y7~4}DLXOML*{2>Ztw}P zJ8O!rF^k6_rp0|J#3mH#jKDBzW&m?H)i{C^4j6Gv;eerlmWV$TYOflZ3DQ)epRdhf zcgUtzF6@Ck(boo2MB}(OrHx6W7}yR~8!2`kJz6hWwy_t}Qh**+<1fWTyizs{F-2b@ z=o#S`3>?&dj6~nxEVCo!U{lIl5Y`OA zk;UiehO)9uROo`KyH%+!p_mDgYzr6=dQ-igs1iaOtEifUAvk_pze}=AGf26BMn=nK z=2gnUspxwX{w9MrFs1@l1G2e?7(3?{9wmnoW!B^#AcU1_zeiD}^x&hbjlEkH%+600 zY2X%ZAoEjAQ=3pAvgu-NWzBhpbXrn4%i-KNR9t%a3%f4^vM(rWsj=}3?}aRIL+|4eOWJ))@XQyw77c4NngZ}gj7O;iLShZ zS8`xl?m>*&Ig0P-SF|C;rZ2&GfNk39R~L(>!BPq!?3ft^7lH6d+{>-6=pG)7k01sm zpi@M1DV3rQc*AL_m58j$;f{1yl?bwp94-#P{v}&Cdk&@*z{?~{s6+QvX^(U;$d{Rn z?`^MZpURaWMvPKS%A>hhi=1+`(UEG4f$LiHS_Gr#VDjh;Ri%M?_ z7=Q6S3YV;)s@`7k@d9nlrpFfkb_yC7E97!Fl|*1q^i58@VSq*n`Sv4-kfEG1MaPW6 zu5{n$ujDddNRoY(sBejuADU+KrFM0;$&yOZ>c}AAiW$=9pN42hMz}E=ka~J>62)Yy zF4~q5PWXyV8=EVmE>PMSaGT@R*kBsus)zYI2{Nj!={12VsJ`u>FU^-WEnPc=F<0*O zpm1PT0If7;^PMOc>KqeCw@L+abShGu-C9|_-1Y!ENJlvzX2p52eAy0Y&w&-Hr7jGr zGD;zmQsAR4sx8i;xf?w+?I-i2Qp;K4Wo1UaQ0MPFw7pa!PEf?8tkhT^zTZu~HvSih zR#%QTTAfx6)CX9Jwp!2~>{>3aa(wqP3QGnZee{YTr_yC(-*01~TUxNVN_t5HNU^biiGx+sM`v^<^{5(|Ltlp7VLzBH zqM(gTWP`v}PEfF<2!$wz!RRT*Nc*Gxik?UIU6YiJV0QSDDw!>sNuDaZNrUV>fPO14VRPjNsl zZ`b%63e3~&;~DwHyU=)Umzt7hc(GWd^D0Tpq;tjHFY&z8eqzh2?nYG1OHF3q~Z92iSfgel+b_bvJD{teVL-N7(Gazj-SE4(%s=2O9vu$;J$qZ)ceD%}~?N z48DF$NeAtS!ob0UXKJmbnIK0yC0r83WJYrBI6}}gpQG_0v7du3c6ClV316LEFK)sJ z?3wZjYz?UyqB``2Li1EE8{h{l{-1xIhwFCWFa0KlI*8wPI*8x&q66lS{fM9D>czQdboyudW8Sbb>3^;b=Tmmy@xJ*C$4+=^Lz{c z$DSKNvY+Y1_=z3rkFPI|*iUKYx*uP=Q@!!$=nnZ~ue%YV{Y+ff++fYOXukCpPPU25C2*H@=kT%3#=qMBk9BV>%aH>GdtDoXBS88 zr#sAb_Pu}i!(HkaJ;3~->*()Y=3jGI?fKCz^#p!**#5EWOV_eZnG?@{(b?*~ucY5~ y!Efl{1YLJ7{yqbLjjiAlo`1vH>K|+Tk^e)_p+9t=f8AUD>TLBzJ|q9J*Zm(s;=0%X diff --git a/panda/board/obj/panda_h7.bin b/panda/board/obj/panda_h7.bin deleted file mode 100755 index dc62831cf8d6ac56e69fe3022bebe5cbfcd468af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71092 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 Ue^4?kd%|dK(41XeJQw)3wg|M{5NtPqbpfqO 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

4ogW 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�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 diff --git a/panda/board/obj/version b/panda/board/obj/version deleted file mode 100644 index 4c96b5a..0000000 --- a/panda/board/obj/version +++ /dev/null @@ -1 +0,0 @@ -DEV-a165aa11-DEBUG \ No newline at end of file diff --git a/panda/board/power_saving.h b/panda/board/power_saving.h new file mode 100644 index 0000000..61541ca --- /dev/null +++ b/panda/board/power_saving.h @@ -0,0 +1,46 @@ +// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM1840, we should never implement any of the available hardware low power modes. +// See rule: CoU_3 + +#define POWER_SAVE_STATUS_DISABLED 0 +#define POWER_SAVE_STATUS_ENABLED 1 + +int power_save_status = POWER_SAVE_STATUS_DISABLED; + +void set_power_save_state(int state) { + + bool is_valid_state = (state == POWER_SAVE_STATUS_ENABLED) || (state == POWER_SAVE_STATUS_DISABLED); + if (is_valid_state && (state != power_save_status)) { + bool enable = false; + if (state == POWER_SAVE_STATUS_ENABLED) { + print("enable power savings\n"); + + // Disable CAN interrupts + if (harness.status == HARNESS_STATUS_FLIPPED) { + llcan_irq_disable(cans[0]); + } else { + llcan_irq_disable(cans[2]); + } + llcan_irq_disable(cans[1]); + } else { + print("disable power savings\n"); + + if (harness.status == HARNESS_STATUS_FLIPPED) { + llcan_irq_enable(cans[0]); + } else { + llcan_irq_enable(cans[2]); + } + llcan_irq_enable(cans[1]); + + enable = true; + } + + current_board->enable_can_transceivers(enable); + + // Switch off IR when in power saving + if(!enable){ + current_board->set_ir_power(0U); + } + + power_save_status = state; + } +} diff --git a/panda/board/provision.h b/panda/board/provision.h new file mode 100644 index 0000000..02768c9 --- /dev/null +++ b/panda/board/provision.h @@ -0,0 +1,13 @@ +// this is where we manage the dongle ID assigned during our +// manufacturing. aside from this, there's a UID for the MCU + +#define PROVISION_CHUNK_LEN 0x20 + +const char unprovisioned_text[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + +void get_provision_chunk(uint8_t *resp) { + (void)memcpy(resp, (uint8_t *)PROVISION_CHUNK_ADDRESS, PROVISION_CHUNK_LEN); + if (memcmp(resp, unprovisioned_text, 0x20) == 0) { + (void)memcpy(resp, "unprovisioned\x00\x00\x00testing123\x00\x00\xa3\xa6\x99\xec", 0x20); + } +} diff --git a/panda/board/safety.h b/panda/board/safety.h new file mode 100644 index 0000000..24ff779 --- /dev/null +++ b/panda/board/safety.h @@ -0,0 +1,708 @@ +#include "safety_declarations.h" +#include "can_definitions.h" + +// include the safety policies. +#include "safety/safety_defaults.h" +#include "safety/safety_honda.h" +#include "safety/safety_toyota.h" +#include "safety/safety_tesla.h" +#include "safety/safety_gm.h" +#include "safety/safety_ford.h" +#include "safety/safety_hyundai.h" +#include "safety/safety_chrysler.h" +#include "safety/safety_subaru.h" +#include "safety/safety_subaru_preglobal.h" +#include "safety/safety_mazda.h" +#include "safety/safety_nissan.h" +#include "safety/safety_volkswagen_mqb.h" +#include "safety/safety_volkswagen_pq.h" +#include "safety/safety_elm327.h" +#include "safety/safety_body.h" + +// CAN-FD only safety modes +#ifdef CANFD +#include "safety/safety_hyundai_canfd.h" +#endif + +// from cereal.car.CarParams.SafetyModel +#define SAFETY_SILENT 0U +#define SAFETY_HONDA_NIDEC 1U +#define SAFETY_TOYOTA 2U +#define SAFETY_ELM327 3U +#define SAFETY_GM 4U +#define SAFETY_HONDA_BOSCH_GIRAFFE 5U +#define SAFETY_FORD 6U +#define SAFETY_HYUNDAI 8U +#define SAFETY_CHRYSLER 9U +#define SAFETY_TESLA 10U +#define SAFETY_SUBARU 11U +#define SAFETY_MAZDA 13U +#define SAFETY_NISSAN 14U +#define SAFETY_VOLKSWAGEN_MQB 15U +#define SAFETY_ALLOUTPUT 17U +#define SAFETY_GM_ASCM 18U +#define SAFETY_NOOUTPUT 19U +#define SAFETY_HONDA_BOSCH 20U +#define SAFETY_VOLKSWAGEN_PQ 21U +#define SAFETY_SUBARU_PREGLOBAL 22U +#define SAFETY_HYUNDAI_LEGACY 23U +#define SAFETY_HYUNDAI_COMMUNITY 24U +#define SAFETY_STELLANTIS 25U +#define SAFETY_FAW 26U +#define SAFETY_BODY 27U +#define SAFETY_HYUNDAI_CANFD 28U + +uint16_t current_safety_mode = SAFETY_SILENT; +uint16_t current_safety_param = 0; +const safety_hooks *current_hooks = &nooutput_hooks; +safety_config current_safety_config; + +bool safety_rx_hook(const CANPacket_t *to_push) { + bool controls_allowed_prev = controls_allowed; + + bool valid = rx_msg_safety_check(to_push, ¤t_safety_config, current_hooks); + if (valid) { + current_hooks->rx(to_push); + } + + // reset mismatches on rising edge of controls_allowed to avoid rare race condition + if (controls_allowed && !controls_allowed_prev) { + heartbeat_engaged_mismatches = 0; + } + + return valid; +} + +bool safety_tx_hook(CANPacket_t *to_send) { + bool whitelisted = msg_allowed(to_send, current_safety_config.tx_msgs, current_safety_config.tx_msgs_len); + if ((current_safety_mode == SAFETY_ALLOUTPUT) || (current_safety_mode == SAFETY_ELM327)) { + whitelisted = true; + } + + const bool safety_allowed = current_hooks->tx(to_send); + return !relay_malfunction && whitelisted && safety_allowed; +} + +int safety_fwd_hook(int bus_num, int addr) { + return (relay_malfunction ? -1 : current_hooks->fwd(bus_num, addr)); +} + +bool get_longitudinal_allowed(void) { + return controls_allowed && !gas_pressed_prev; +} + +// Given a CRC-8 poly, generate a static lookup table to use with a fast CRC-8 +// algorithm. Called at init time for safety modes using CRC-8. +void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]) { + for (uint16_t i = 0U; i <= 0xFFU; i++) { + uint8_t crc = (uint8_t)i; + for (int j = 0; j < 8; j++) { + if ((crc & 0x80U) != 0U) { + crc = (uint8_t)((crc << 1) ^ poly); + } else { + crc <<= 1; + } + } + crc_lut[i] = crc; + } +} + +void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]) { + for (uint16_t i = 0; i < 256U; i++) { + uint16_t crc = i << 8U; + for (uint16_t j = 0; j < 8U; j++) { + if ((crc & 0x8000U) != 0U) { + crc = (uint16_t)((crc << 1) ^ poly); + } else { + crc <<= 1; + } + } + crc_lut[i] = crc; + } +} + +bool msg_allowed(const CANPacket_t *to_send, const CanMsg msg_list[], int len) { + int addr = GET_ADDR(to_send); + int bus = GET_BUS(to_send); + int length = GET_LEN(to_send); + + bool allowed = false; + for (int i = 0; i < len; i++) { + if ((addr == msg_list[i].addr) && (bus == msg_list[i].bus) && (length == msg_list[i].len)) { + allowed = true; + break; + } + } + return allowed; +} + +int get_addr_check_index(const CANPacket_t *to_push, RxCheck addr_list[], const int len) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + int length = GET_LEN(to_push); + + int index = -1; + for (int i = 0; i < len; i++) { + // if multiple msgs are allowed, determine which one is present on the bus + if (!addr_list[i].status.msg_seen) { + for (uint8_t j = 0U; (j < MAX_ADDR_CHECK_MSGS) && (addr_list[i].msg[j].addr != 0); j++) { + if ((addr == addr_list[i].msg[j].addr) && (bus == addr_list[i].msg[j].bus) && + (length == addr_list[i].msg[j].len)) { + addr_list[i].status.index = j; + addr_list[i].status.msg_seen = true; + break; + } + } + } + + if (addr_list[i].status.msg_seen) { + int idx = addr_list[i].status.index; + if ((addr == addr_list[i].msg[idx].addr) && (bus == addr_list[i].msg[idx].bus) && + (length == addr_list[i].msg[idx].len)) { + index = i; + break; + } + } + } + return index; +} + +// 1Hz safety function called by main. Now just a check for lagging safety messages +void safety_tick(const safety_config *cfg) { + bool rx_checks_invalid = false; + uint32_t ts = microsecond_timer_get(); + if (cfg != NULL) { + for (int i=0; i < cfg->rx_checks_len; i++) { + uint32_t elapsed_time = get_ts_elapsed(ts, cfg->rx_checks[i].status.last_timestamp); + // lag threshold is max of: 1s and MAX_MISSED_MSGS * expected timestep. + // Quite conservative to not risk false triggers. + // 2s of lag is worse case, since the function is called at 1Hz + uint32_t timestep = 1e6 / cfg->rx_checks[i].msg[cfg->rx_checks[i].status.index].frequency; + bool lagging = elapsed_time > MAX(timestep * MAX_MISSED_MSGS, 1e6); + cfg->rx_checks[i].status.lagging = lagging; + if (lagging) { + controls_allowed = false; + } + + if (lagging || !is_msg_valid(cfg->rx_checks, i)) { + rx_checks_invalid = true; + } + } + } + + safety_rx_checks_invalid = rx_checks_invalid; +} + +void update_counter(RxCheck addr_list[], int index, uint8_t counter) { + if (index != -1) { + uint8_t expected_counter = (addr_list[index].status.last_counter + 1U) % (addr_list[index].msg[addr_list[index].status.index].max_counter + 1U); + addr_list[index].status.wrong_counters += (expected_counter == counter) ? -1 : 1; + addr_list[index].status.wrong_counters = CLAMP(addr_list[index].status.wrong_counters, 0, MAX_WRONG_COUNTERS); + addr_list[index].status.last_counter = counter; + } +} + +bool is_msg_valid(RxCheck addr_list[], int index) { + bool valid = true; + if (index != -1) { + if (!addr_list[index].status.valid_checksum || !addr_list[index].status.valid_quality_flag || (addr_list[index].status.wrong_counters >= MAX_WRONG_COUNTERS)) { + valid = false; + controls_allowed = false; + } + } + return valid; +} + +void update_addr_timestamp(RxCheck addr_list[], int index) { + if (index != -1) { + uint32_t ts = microsecond_timer_get(); + addr_list[index].status.last_timestamp = ts; + } +} + +bool rx_msg_safety_check(const CANPacket_t *to_push, + const safety_config *cfg, + const safety_hooks *safety_hooks) { + + int index = get_addr_check_index(to_push, cfg->rx_checks, cfg->rx_checks_len); + update_addr_timestamp(cfg->rx_checks, index); + + if (index != -1) { + // checksum check + if ((safety_hooks->get_checksum != NULL) && (safety_hooks->compute_checksum != NULL) && cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].check_checksum) { + uint32_t checksum = safety_hooks->get_checksum(to_push); + uint32_t checksum_comp = safety_hooks->compute_checksum(to_push); + cfg->rx_checks[index].status.valid_checksum = checksum_comp == checksum; + } else { + cfg->rx_checks[index].status.valid_checksum = true; + } + + // counter check (max_counter == 0 means skip check) + if ((safety_hooks->get_counter != NULL) && (cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].max_counter > 0U)) { + uint8_t counter = safety_hooks->get_counter(to_push); + update_counter(cfg->rx_checks, index, counter); + } else { + cfg->rx_checks[index].status.wrong_counters = 0U; + } + + // quality flag check + if ((safety_hooks->get_quality_flag_valid != NULL) && cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].quality_flag) { + cfg->rx_checks[index].status.valid_quality_flag = safety_hooks->get_quality_flag_valid(to_push); + } else { + cfg->rx_checks[index].status.valid_quality_flag = true; + } + } + return is_msg_valid(cfg->rx_checks, index); +} + +void generic_rx_checks(bool stock_ecu_detected) { + // exit controls on rising edge of gas press + if (gas_pressed && !gas_pressed_prev && !(alternative_experience & ALT_EXP_DISABLE_DISENGAGE_ON_GAS)) { + controls_allowed = false; + } + gas_pressed_prev = gas_pressed; + + // exit controls on rising edge of brake press + if (brake_pressed && (!brake_pressed_prev || vehicle_moving)) { + controls_allowed = false; + } + brake_pressed_prev = brake_pressed; + + // exit controls on rising edge of regen paddle + if (regen_braking && (!regen_braking_prev || vehicle_moving)) { + controls_allowed = false; + } + regen_braking_prev = regen_braking; + + // check if stock ECU is on bus broken by car harness + if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && stock_ecu_detected && !gm_skip_relay_check) { + relay_malfunction_set(); + } +} + +void relay_malfunction_set(void) { + relay_malfunction = true; + fault_occurred(FAULT_RELAY_MALFUNCTION); +} + +void relay_malfunction_reset(void) { + relay_malfunction = false; + fault_recovered(FAULT_RELAY_MALFUNCTION); +} + +typedef struct { + uint16_t id; + const safety_hooks *hooks; +} safety_hook_config; + +const safety_hook_config safety_hook_registry[] = { + {SAFETY_SILENT, &nooutput_hooks}, + {SAFETY_HONDA_NIDEC, &honda_nidec_hooks}, + {SAFETY_TOYOTA, &toyota_hooks}, + {SAFETY_ELM327, &elm327_hooks}, + {SAFETY_GM, &gm_hooks}, + {SAFETY_HONDA_BOSCH, &honda_bosch_hooks}, + {SAFETY_HYUNDAI, &hyundai_hooks}, + {SAFETY_CHRYSLER, &chrysler_hooks}, + {SAFETY_SUBARU, &subaru_hooks}, + {SAFETY_VOLKSWAGEN_MQB, &volkswagen_mqb_hooks}, + {SAFETY_NISSAN, &nissan_hooks}, + {SAFETY_NOOUTPUT, &nooutput_hooks}, + {SAFETY_HYUNDAI_LEGACY, &hyundai_legacy_hooks}, + {SAFETY_MAZDA, &mazda_hooks}, + {SAFETY_BODY, &body_hooks}, + {SAFETY_FORD, &ford_hooks}, +#ifdef CANFD + {SAFETY_HYUNDAI_CANFD, &hyundai_canfd_hooks}, +#endif +#ifdef ALLOW_DEBUG + {SAFETY_TESLA, &tesla_hooks}, + {SAFETY_SUBARU_PREGLOBAL, &subaru_preglobal_hooks}, + {SAFETY_VOLKSWAGEN_PQ, &volkswagen_pq_hooks}, + {SAFETY_ALLOUTPUT, &alloutput_hooks}, +#endif +}; + +int set_safety_hooks(uint16_t mode, uint16_t param) { + // reset state set by safety mode + safety_mode_cnt = 0U; + relay_malfunction = false; + enable_gas_interceptor = false; + gas_interceptor_prev = 0; + gas_pressed = false; + gas_pressed_prev = false; + brake_pressed = false; + brake_pressed_prev = false; + regen_braking = false; + regen_braking_prev = false; + cruise_engaged_prev = false; + vehicle_moving = false; + acc_main_on = false; + cruise_button_prev = 0; + desired_torque_last = 0; + rt_torque_last = 0; + ts_angle_last = 0; + desired_angle_last = 0; + ts_torque_check_last = 0; + ts_steer_req_mismatch_last = 0; + valid_steer_req_count = 0; + invalid_steer_req_count = 0; + + // reset samples + reset_sample(&vehicle_speed); + reset_sample(&torque_meas); + reset_sample(&torque_driver); + reset_sample(&angle_meas); + + controls_allowed = false; + relay_malfunction_reset(); + safety_rx_checks_invalid = false; + + current_safety_config.rx_checks = NULL; + current_safety_config.rx_checks_len = 0; + current_safety_config.tx_msgs = NULL; + current_safety_config.tx_msgs_len = 0; + + int set_status = -1; // not set + int hook_config_count = sizeof(safety_hook_registry) / sizeof(safety_hook_config); + for (int i = 0; i < hook_config_count; i++) { + if (safety_hook_registry[i].id == mode) { + current_hooks = safety_hook_registry[i].hooks; + current_safety_mode = mode; + current_safety_param = param; + set_status = 0; // set + } + } + if ((set_status == 0) && (current_hooks->init != NULL)) { + safety_config cfg = current_hooks->init(param); + current_safety_config.rx_checks = cfg.rx_checks; + current_safety_config.rx_checks_len = cfg.rx_checks_len; + current_safety_config.tx_msgs = cfg.tx_msgs; + current_safety_config.tx_msgs_len = cfg.tx_msgs_len; + // reset all dynamic fields in addr struct + for (int j = 0; j < current_safety_config.rx_checks_len; j++) { + current_safety_config.rx_checks[j].status = (RxStatus){0}; + } + } + return set_status; +} + +// convert a trimmed integer to signed 32 bit int +int to_signed(int d, int bits) { + int d_signed = d; + int max_value = (1 << MAX((bits - 1), 0)); + if (d >= max_value) { + d_signed = d - (1 << MAX(bits, 0)); + } + return d_signed; +} + +// given a new sample, update the sample_t struct +void update_sample(struct sample_t *sample, int sample_new) { + for (int i = MAX_SAMPLE_VALS - 1; i > 0; i--) { + sample->values[i] = sample->values[i-1]; + } + sample->values[0] = sample_new; + + // get the minimum and maximum measured samples + sample->min = sample->values[0]; + sample->max = sample->values[0]; + for (int i = 1; i < MAX_SAMPLE_VALS; i++) { + if (sample->values[i] < sample->min) { + sample->min = sample->values[i]; + } + if (sample->values[i] > sample->max) { + sample->max = sample->values[i]; + } + } +} + +// resets values and min/max for sample_t struct +void reset_sample(struct sample_t *sample) { + for (int i = 0; i < MAX_SAMPLE_VALS; i++) { + sample->values[i] = 0; + } + update_sample(sample, 0); +} + +bool max_limit_check(int val, const int MAX_VAL, const int MIN_VAL) { + return (val > MAX_VAL) || (val < MIN_VAL); +} + +// check that commanded torque value isn't too far from measured +bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, + const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR) { + + // *** val rate limit check *** + int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; + int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; + + // if we've exceeded the meas val, we must start moving toward 0 + int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, MAX(val_meas->max, 0) + MAX_ERROR)); + int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, MIN(val_meas->min, 0) - MAX_ERROR)); + + // check for violation + return max_limit_check(val, highest_allowed, lowest_allowed); +} + +// check that commanded value isn't fighting against driver +bool driver_limit_check(int val, int val_last, const struct sample_t *val_driver, + const int MAX_VAL, const int MAX_RATE_UP, const int MAX_RATE_DOWN, + const int MAX_ALLOWANCE, const int DRIVER_FACTOR) { + + // torque delta/rate limits + int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; + int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; + + // driver + int driver_max_limit = MAX_VAL + (MAX_ALLOWANCE + val_driver->max) * DRIVER_FACTOR; + int driver_min_limit = -MAX_VAL + (-MAX_ALLOWANCE + val_driver->min) * DRIVER_FACTOR; + + // if we've exceeded the applied torque, we must start moving toward 0 + int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, + MAX(driver_max_limit, 0))); + int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, + MIN(driver_min_limit, 0))); + + // check for violation + return max_limit_check(val, highest_allowed, lowest_allowed); +} + + +// real time check, mainly used for steer torque rate limiter +bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA) { + + // *** torque real time rate limit check *** + int highest_val = MAX(val_last, 0) + MAX_RT_DELTA; + int lowest_val = MIN(val_last, 0) - MAX_RT_DELTA; + + // check for violation + return max_limit_check(val, highest_val, lowest_val); +} + + +// interp function that holds extreme values +float interpolate(struct lookup_t xy, float x) { + + int size = sizeof(xy.x) / sizeof(xy.x[0]); + float ret = xy.y[size - 1]; // default output is last point + + // x is lower than the first point in the x array. Return the first point + if (x <= xy.x[0]) { + ret = xy.y[0]; + + } else { + // find the index such that (xy.x[i] <= x < xy.x[i+1]) and linearly interp + for (int i=0; i < (size - 1); i++) { + if (x < xy.x[i+1]) { + float x0 = xy.x[i]; + float y0 = xy.y[i]; + float dx = xy.x[i+1] - x0; + float dy = xy.y[i+1] - y0; + // dx should not be zero as xy.x is supposed to be monotonic + dx = MAX(dx, 0.0001); + ret = (dy * (x - x0) / dx) + y0; + break; + } + } + } + return ret; +} + +int ROUND(float val) { + return val + ((val > 0.0) ? 0.5 : -0.5); +} + +// Safety checks for longitudinal actuation +bool longitudinal_accel_checks(int desired_accel, const LongitudinalLimits limits) { + bool accel_valid = get_longitudinal_allowed() && !max_limit_check(desired_accel, limits.max_accel, limits.min_accel); + bool accel_inactive = desired_accel == limits.inactive_accel; + return !(accel_valid || accel_inactive); +} + +bool longitudinal_speed_checks(int desired_speed, const LongitudinalLimits limits) { + return !get_longitudinal_allowed() && (desired_speed != limits.inactive_speed); +} + +bool longitudinal_transmission_rpm_checks(int desired_transmission_rpm, const LongitudinalLimits limits) { + bool transmission_rpm_valid = get_longitudinal_allowed() && !max_limit_check(desired_transmission_rpm, limits.max_transmission_rpm, limits.min_transmission_rpm); + bool transmission_rpm_inactive = desired_transmission_rpm == limits.inactive_transmission_rpm; + return !(transmission_rpm_valid || transmission_rpm_inactive); +} + +bool longitudinal_gas_checks(int desired_gas, const LongitudinalLimits limits) { + bool gas_valid = get_longitudinal_allowed() && !max_limit_check(desired_gas, limits.max_gas, limits.min_gas); + bool gas_inactive = desired_gas == limits.inactive_gas; + return !(gas_valid || gas_inactive); +} + +bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limits) { + bool violation = false; + violation |= !get_longitudinal_allowed() && (desired_brake != 0); + violation |= desired_brake > limits.max_brake; + return violation; +} + +bool longitudinal_interceptor_checks(const CANPacket_t *to_send) { + return (!get_longitudinal_allowed() || brake_pressed_prev) && (GET_BYTE(to_send, 0) || GET_BYTE(to_send, 1)); +} + +// Safety checks for torque-based steering commands +bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLimits limits) { + bool violation = false; + uint32_t ts = microsecond_timer_get(); + + bool aol_allowed = acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL); + if (controls_allowed) { + // acc main must be on if controls are allowed + acc_main_on = controls_allowed; + } + + if (controls_allowed || aol_allowed) { + // *** global torque limit check *** + violation |= max_limit_check(desired_torque, limits.max_steer, -limits.max_steer); + + // *** torque rate limit check *** + if (limits.type == TorqueDriverLimited) { + violation |= driver_limit_check(desired_torque, desired_torque_last, &torque_driver, + limits.max_steer, limits.max_rate_up, limits.max_rate_down, + limits.driver_torque_allowance, limits.driver_torque_factor); + } else { + violation |= dist_to_meas_check(desired_torque, desired_torque_last, &torque_meas, + limits.max_rate_up, limits.max_rate_down, limits.max_torque_error); + } + desired_torque_last = desired_torque; + + // *** torque real time rate limit check *** + violation |= rt_rate_limit_check(desired_torque, rt_torque_last, limits.max_rt_delta); + + // every RT_INTERVAL set the new limits + uint32_t ts_elapsed = get_ts_elapsed(ts, ts_torque_check_last); + if (ts_elapsed > limits.max_rt_interval) { + rt_torque_last = desired_torque; + ts_torque_check_last = ts; + } + } + + // no torque if controls is not allowed + if (!(controls_allowed || aol_allowed) && (desired_torque != 0)) { + violation = true; + } + + // certain safety modes set their steer request bit low for one or more frame at a + // predefined max frequency to avoid steering faults in certain situations + bool steer_req_mismatch = (steer_req == 0) && (desired_torque != 0); + if (!limits.has_steer_req_tolerance) { + if (steer_req_mismatch) { + violation = true; + } + + } else { + if (steer_req_mismatch) { + if (invalid_steer_req_count == 0) { + // disallow torque cut if not enough recent matching steer_req messages + if (valid_steer_req_count < limits.min_valid_request_frames) { + violation = true; + } + + // or we've cut torque too recently in time + uint32_t ts_elapsed = get_ts_elapsed(ts, ts_steer_req_mismatch_last); + if (ts_elapsed < limits.min_valid_request_rt_interval) { + violation = true; + } + } else { + // or we're cutting more frames consecutively than allowed + if (invalid_steer_req_count >= limits.max_invalid_request_frames) { + violation = true; + } + } + + valid_steer_req_count = 0; + ts_steer_req_mismatch_last = ts; + invalid_steer_req_count = MIN(invalid_steer_req_count + 1, limits.max_invalid_request_frames); + } else { + valid_steer_req_count = MIN(valid_steer_req_count + 1, limits.min_valid_request_frames); + invalid_steer_req_count = 0; + } + } + + // reset to 0 if either controls is not allowed or there's a violation + if (violation || !(controls_allowed || aol_allowed)) { + valid_steer_req_count = 0; + invalid_steer_req_count = 0; + desired_torque_last = 0; + rt_torque_last = 0; + ts_torque_check_last = ts; + ts_steer_req_mismatch_last = ts; + } + + return violation; +} + +// Safety checks for angle-based steering commands +bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const SteeringLimits limits) { + bool violation = false; + bool aol_allowed = acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL); + if (controls_allowed) { + // acc main must be on if controls are allowed + acc_main_on = controls_allowed; + } + + if ((controls_allowed || aol_allowed) && steer_control_enabled) { + // convert floating point angle rate limits to integers in the scale of the desired angle on CAN, + // add 1 to not false trigger the violation. also fudge the speed by 1 m/s so rate limits are + // always slightly above openpilot's in case we read an updated speed in between angle commands + // TODO: this speed fudge can be much lower, look at data to determine the lowest reasonable offset + int delta_angle_up = (interpolate(limits.angle_rate_up_lookup, (vehicle_speed.min / VEHICLE_SPEED_FACTOR) - 1.) * limits.angle_deg_to_can) + 1.; + int delta_angle_down = (interpolate(limits.angle_rate_down_lookup, (vehicle_speed.min / VEHICLE_SPEED_FACTOR) - 1.) * limits.angle_deg_to_can) + 1.; + + // allow down limits at zero since small floats will be rounded to 0 + int highest_desired_angle = desired_angle_last + ((desired_angle_last > 0) ? delta_angle_up : delta_angle_down); + int lowest_desired_angle = desired_angle_last - ((desired_angle_last >= 0) ? delta_angle_down : delta_angle_up); + + // check that commanded angle value isn't too far from measured, used to limit torque for some safety modes + // ensure we start moving in direction of meas while respecting rate limits if error is exceeded + if (limits.enforce_angle_error && ((vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR) > limits.angle_error_min_speed)) { + // the rate limits above are liberally above openpilot's to avoid false positives. + // likewise, allow a lower rate for moving towards meas when error is exceeded + int delta_angle_up_lower = interpolate(limits.angle_rate_up_lookup, (vehicle_speed.max / VEHICLE_SPEED_FACTOR) + 1.) * limits.angle_deg_to_can; + int delta_angle_down_lower = interpolate(limits.angle_rate_down_lookup, (vehicle_speed.max / VEHICLE_SPEED_FACTOR) + 1.) * limits.angle_deg_to_can; + + int highest_desired_angle_lower = desired_angle_last + ((desired_angle_last > 0) ? delta_angle_up_lower : delta_angle_down_lower); + int lowest_desired_angle_lower = desired_angle_last - ((desired_angle_last >= 0) ? delta_angle_down_lower : delta_angle_up_lower); + + lowest_desired_angle = MIN(MAX(lowest_desired_angle, angle_meas.min - limits.max_angle_error - 1), highest_desired_angle_lower); + highest_desired_angle = MAX(MIN(highest_desired_angle, angle_meas.max + limits.max_angle_error + 1), lowest_desired_angle_lower); + + // don't enforce above the max steer + lowest_desired_angle = CLAMP(lowest_desired_angle, -limits.max_steer, limits.max_steer); + highest_desired_angle = CLAMP(highest_desired_angle, -limits.max_steer, limits.max_steer); + } + + // check for violation; + violation |= max_limit_check(desired_angle, highest_desired_angle, lowest_desired_angle); + } + desired_angle_last = desired_angle; + + // Angle should either be 0 or same as current angle while not steering + if (!steer_control_enabled) { + violation |= (limits.inactive_angle_is_zero ? (desired_angle != 0) : + max_limit_check(desired_angle, angle_meas.max + 1, angle_meas.min - 1)); + } + + // No angle control allowed when controls are not allowed + violation |= !(controls_allowed || aol_allowed) && steer_control_enabled; + + return violation; +} + +void pcm_cruise_check(bool cruise_engaged) { + // Enter controls on rising edge of stock ACC, exit controls if stock ACC disengages + if (!cruise_engaged) { + controls_allowed = false; + } + if (cruise_engaged && !cruise_engaged_prev) { + controls_allowed = true; + } + cruise_engaged_prev = cruise_engaged; +} diff --git a/panda/board/safety/safety_body.h b/panda/board/safety/safety_body.h new file mode 100644 index 0000000..2ebca28 --- /dev/null +++ b/panda/board/safety/safety_body.h @@ -0,0 +1,46 @@ +const CanMsg BODY_TX_MSGS[] = {{0x250, 0, 8}, {0x250, 0, 6}, {0x251, 0, 5}, // body + {0x350, 0, 8}, {0x350, 0, 6}, {0x351, 0, 5}, // knee + {0x1, 0, 8}}; // CAN flasher + +RxCheck body_rx_checks[] = { + {.msg = {{0x201, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, +}; + +static void body_rx_hook(const CANPacket_t *to_push) { + // body is never at standstill + vehicle_moving = true; + + if (GET_ADDR(to_push) == 0x201U) { + controls_allowed = true; + } +} + +static bool body_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + int len = GET_LEN(to_send); + + if (!controls_allowed && (addr != 0x1)) { + tx = false; + } + + // Allow going into CAN flashing mode for base & knee even if controls are not allowed + bool flash_msg = ((addr == 0x250) || (addr == 0x350)) && (len == 8); + if (!controls_allowed && (GET_BYTES(to_send, 0, 4) == 0xdeadfaceU) && (GET_BYTES(to_send, 4, 4) == 0x0ab00b1eU) && flash_msg) { + tx = true; + } + + return tx; +} + +static safety_config body_init(uint16_t param) { + UNUSED(param); + return BUILD_SAFETY_CFG(body_rx_checks, BODY_TX_MSGS); +} + +const safety_hooks body_hooks = { + .init = body_init, + .rx = body_rx_hook, + .tx = body_tx_hook, + .fwd = default_fwd_hook, +}; diff --git a/panda/board/safety/safety_chrysler.h b/panda/board/safety/safety_chrysler.h new file mode 100644 index 0000000..b80e3e8 --- /dev/null +++ b/panda/board/safety/safety_chrysler.h @@ -0,0 +1,296 @@ +const SteeringLimits CHRYSLER_STEERING_LIMITS = { + .max_steer = 261, + .max_rt_delta = 112, + .max_rt_interval = 250000, + .max_rate_up = 3, + .max_rate_down = 3, + .max_torque_error = 80, + .type = TorqueMotorLimited, +}; + +const SteeringLimits CHRYSLER_RAM_DT_STEERING_LIMITS = { + .max_steer = 350, + .max_rt_delta = 112, + .max_rt_interval = 250000, + .max_rate_up = 6, + .max_rate_down = 6, + .max_torque_error = 80, + .type = TorqueMotorLimited, +}; + +const SteeringLimits CHRYSLER_RAM_HD_STEERING_LIMITS = { + .max_steer = 361, + .max_rt_delta = 182, + .max_rt_interval = 250000, + .max_rate_up = 14, + .max_rate_down = 14, + .max_torque_error = 80, + .type = TorqueMotorLimited, +}; + +typedef struct { + const int EPS_2; + const int ESP_1; + const int ESP_8; + const int ECM_5; + const int DAS_3; + const int DAS_6; + const int LKAS_COMMAND; + const int CRUISE_BUTTONS; +} ChryslerAddrs; + +// CAN messages for Chrysler/Jeep platforms +const ChryslerAddrs CHRYSLER_ADDRS = { + .EPS_2 = 0x220, // EPS driver input torque + .ESP_1 = 0x140, // Brake pedal and vehicle speed + .ESP_8 = 0x11C, // Brake pedal and vehicle speed + .ECM_5 = 0x22F, // Throttle position sensor + .DAS_3 = 0x1F4, // ACC engagement states from DASM + .DAS_6 = 0x2A6, // LKAS HUD and auto headlight control from DASM + .LKAS_COMMAND = 0x292, // LKAS controls from DASM + .CRUISE_BUTTONS = 0x23B, // Cruise control buttons +}; + +// CAN messages for the 5th gen RAM DT platform +const ChryslerAddrs CHRYSLER_RAM_DT_ADDRS = { + .EPS_2 = 0x31, // EPS driver input torque + .ESP_1 = 0x83, // Brake pedal and vehicle speed + .ESP_8 = 0x79, // Brake pedal and vehicle speed + .ECM_5 = 0x9D, // Throttle position sensor + .DAS_3 = 0x99, // ACC engagement states from DASM + .DAS_6 = 0xFA, // LKAS HUD and auto headlight control from DASM + .LKAS_COMMAND = 0xA6, // LKAS controls from DASM + .CRUISE_BUTTONS = 0xB1, // Cruise control buttons +}; + +// CAN messages for the 5th gen RAM HD platform +const ChryslerAddrs CHRYSLER_RAM_HD_ADDRS = { + .EPS_2 = 0x220, // EPS driver input torque + .ESP_1 = 0x140, // Brake pedal and vehicle speed + .ESP_8 = 0x11C, // Brake pedal and vehicle speed + .ECM_5 = 0x22F, // Throttle position sensor + .DAS_3 = 0x1F4, // ACC engagement states from DASM + .DAS_6 = 0x275, // LKAS HUD and auto headlight control from DASM + .LKAS_COMMAND = 0x276, // LKAS controls from DASM + .CRUISE_BUTTONS = 0x23A, // Cruise control buttons +}; + +const CanMsg CHRYSLER_TX_MSGS[] = { + {CHRYSLER_ADDRS.CRUISE_BUTTONS, 0, 3}, + {CHRYSLER_ADDRS.LKAS_COMMAND, 0, 6}, + {CHRYSLER_ADDRS.DAS_6, 0, 8}, +}; + +const CanMsg CHRYSLER_RAM_DT_TX_MSGS[] = { + {CHRYSLER_RAM_DT_ADDRS.CRUISE_BUTTONS, 2, 3}, + {CHRYSLER_RAM_DT_ADDRS.LKAS_COMMAND, 0, 8}, + {CHRYSLER_RAM_DT_ADDRS.DAS_6, 0, 8}, +}; + +const CanMsg CHRYSLER_RAM_HD_TX_MSGS[] = { + {CHRYSLER_RAM_HD_ADDRS.CRUISE_BUTTONS, 2, 3}, + {CHRYSLER_RAM_HD_ADDRS.LKAS_COMMAND, 0, 8}, + {CHRYSLER_RAM_HD_ADDRS.DAS_6, 0, 8}, +}; + +RxCheck chrysler_rx_checks[] = { + {.msg = {{CHRYSLER_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + //{.msg = {{ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}}}, + {.msg = {{514, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_ADDRS.DAS_3, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +RxCheck chrysler_ram_dt_rx_checks[] = { + {.msg = {{CHRYSLER_RAM_DT_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_DT_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_DT_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_DT_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_DT_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +RxCheck chrysler_ram_hd_rx_checks[] = { + {.msg = {{CHRYSLER_RAM_HD_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + + + +const uint32_t CHRYSLER_PARAM_RAM_DT = 1U; // set for Ram DT platform +const uint32_t CHRYSLER_PARAM_RAM_HD = 2U; // set for Ram HD platform + +enum { + CHRYSLER_RAM_DT, + CHRYSLER_RAM_HD, + CHRYSLER_PACIFICA, // plus Jeep +} chrysler_platform = CHRYSLER_PACIFICA; +const ChryslerAddrs *chrysler_addrs = &CHRYSLER_ADDRS; + +static uint32_t chrysler_get_checksum(const CANPacket_t *to_push) { + int checksum_byte = GET_LEN(to_push) - 1U; + return (uint8_t)(GET_BYTE(to_push, checksum_byte)); +} + +static uint32_t chrysler_compute_checksum(const CANPacket_t *to_push) { + // TODO: clean this up + // http://illmatics.com/Remote%20Car%20Hacking.pdf + uint8_t checksum = 0xFFU; + int len = GET_LEN(to_push); + for (int j = 0; j < (len - 1); j++) { + uint8_t shift = 0x80U; + uint8_t curr = (uint8_t)GET_BYTE(to_push, j); + for (int i=0; i<8; i++) { + uint8_t bit_sum = curr & shift; + uint8_t temp_chk = checksum & 0x80U; + if (bit_sum != 0U) { + bit_sum = 0x1C; + if (temp_chk != 0U) { + bit_sum = 1; + } + checksum = checksum << 1; + temp_chk = checksum | 1U; + bit_sum ^= temp_chk; + } else { + if (temp_chk != 0U) { + bit_sum = 0x1D; + } + checksum = checksum << 1; + bit_sum ^= checksum; + } + checksum = bit_sum; + shift = shift >> 1; + } + } + return (uint8_t)(~checksum); +} + +static uint8_t chrysler_get_counter(const CANPacket_t *to_push) { + return (uint8_t)(GET_BYTE(to_push, 6) >> 4); +} + +static void chrysler_rx_hook(const CANPacket_t *to_push) { + const int bus = GET_BUS(to_push); + const int addr = GET_ADDR(to_push); + + // Measured EPS torque + if ((bus == 0) && (addr == chrysler_addrs->EPS_2)) { + int torque_meas_new = ((GET_BYTE(to_push, 4) & 0x7U) << 8) + GET_BYTE(to_push, 5) - 1024U; + update_sample(&torque_meas, torque_meas_new); + } + + // enter controls on rising edge of ACC, exit controls on ACC off + const int das_3_bus = (chrysler_platform == CHRYSLER_PACIFICA) ? 0 : 2; + if ((bus == das_3_bus) && (addr == chrysler_addrs->DAS_3)) { + acc_main_on = GET_BIT(to_push, 20U); + bool cruise_engaged = GET_BIT(to_push, 21U); + pcm_cruise_check(cruise_engaged); + } + + // TODO: use the same message for both + // update vehicle moving + if ((chrysler_platform != CHRYSLER_PACIFICA) && (bus == 0) && (addr == chrysler_addrs->ESP_8)) { + vehicle_moving = ((GET_BYTE(to_push, 4) << 8) + GET_BYTE(to_push, 5)) != 0U; + } + if ((chrysler_platform == CHRYSLER_PACIFICA) && (bus == 0) && (addr == 514)) { + int speed_l = (GET_BYTE(to_push, 0) << 4) + (GET_BYTE(to_push, 1) >> 4); + int speed_r = (GET_BYTE(to_push, 2) << 4) + (GET_BYTE(to_push, 3) >> 4); + vehicle_moving = (speed_l != 0) || (speed_r != 0); + } + + // exit controls on rising edge of gas press + if ((bus == 0) && (addr == chrysler_addrs->ECM_5)) { + gas_pressed = GET_BYTE(to_push, 0U) != 0U; + } + + // exit controls on rising edge of brake press + if ((bus == 0) && (addr == chrysler_addrs->ESP_1)) { + brake_pressed = ((GET_BYTE(to_push, 0U) & 0xFU) >> 2U) == 1U; + } + + generic_rx_checks((bus == 0) && (addr == chrysler_addrs->LKAS_COMMAND)); +} + +static bool chrysler_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + + // STEERING + if (addr == chrysler_addrs->LKAS_COMMAND) { + int start_byte = (chrysler_platform == CHRYSLER_PACIFICA) ? 0 : 1; + int desired_torque = ((GET_BYTE(to_send, start_byte) & 0x7U) << 8) | GET_BYTE(to_send, start_byte + 1); + desired_torque -= 1024; + + const SteeringLimits limits = (chrysler_platform == CHRYSLER_PACIFICA) ? CHRYSLER_STEERING_LIMITS : + (chrysler_platform == CHRYSLER_RAM_DT) ? CHRYSLER_RAM_DT_STEERING_LIMITS : CHRYSLER_RAM_HD_STEERING_LIMITS; + + bool steer_req = (chrysler_platform == CHRYSLER_PACIFICA) ? GET_BIT(to_send, 4U) : (GET_BYTE(to_send, 3) & 0x7U) == 2U; + if (steer_torque_cmd_checks(desired_torque, steer_req, limits)) { + tx = false; + } + } + + // FORCE CANCEL: only the cancel button press is allowed + if (addr == chrysler_addrs->CRUISE_BUTTONS) { + const bool is_cancel = GET_BYTE(to_send, 0) == 1U; + const bool is_resume = GET_BYTE(to_send, 0) == 0x10U; + const bool allowed = is_cancel || (is_resume && controls_allowed); + if (!allowed) { + tx = false; + } + } + + return tx; +} + +static int chrysler_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + // forward to camera + if (bus_num == 0) { + bus_fwd = 2; + } + + // forward all messages from camera except LKAS messages + const bool is_lkas = ((addr == chrysler_addrs->LKAS_COMMAND) || (addr == chrysler_addrs->DAS_6)); + if ((bus_num == 2) && !is_lkas){ + bus_fwd = 0; + } + + return bus_fwd; +} + +static safety_config chrysler_init(uint16_t param) { + safety_config ret; + + bool enable_ram_dt = GET_FLAG(param, CHRYSLER_PARAM_RAM_DT); + if (enable_ram_dt) { + chrysler_platform = CHRYSLER_RAM_DT; + chrysler_addrs = &CHRYSLER_RAM_DT_ADDRS; + ret = BUILD_SAFETY_CFG(chrysler_ram_dt_rx_checks, CHRYSLER_RAM_DT_TX_MSGS); +#ifdef ALLOW_DEBUG + } else if (GET_FLAG(param, CHRYSLER_PARAM_RAM_HD)) { + chrysler_platform = CHRYSLER_RAM_HD; + chrysler_addrs = &CHRYSLER_RAM_HD_ADDRS; + ret = BUILD_SAFETY_CFG(chrysler_ram_hd_rx_checks, CHRYSLER_RAM_HD_TX_MSGS); +#endif + } else { + chrysler_platform = CHRYSLER_PACIFICA; + chrysler_addrs = &CHRYSLER_ADDRS; + ret = BUILD_SAFETY_CFG(chrysler_rx_checks, CHRYSLER_TX_MSGS); + } + return ret; +} + +const safety_hooks chrysler_hooks = { + .init = chrysler_init, + .rx = chrysler_rx_hook, + .tx = chrysler_tx_hook, + .fwd = chrysler_fwd_hook, + .get_counter = chrysler_get_counter, + .get_checksum = chrysler_get_checksum, + .compute_checksum = chrysler_compute_checksum, +}; diff --git a/panda/board/safety/safety_defaults.h b/panda/board/safety/safety_defaults.h new file mode 100644 index 0000000..6c47dba --- /dev/null +++ b/panda/board/safety/safety_defaults.h @@ -0,0 +1,68 @@ +void default_rx_hook(const CANPacket_t *to_push) { + UNUSED(to_push); +} + +// *** no output safety mode *** + +static safety_config nooutput_init(uint16_t param) { + UNUSED(param); + return (safety_config){NULL, 0, NULL, 0}; +} + +static bool nooutput_tx_hook(const CANPacket_t *to_send) { + UNUSED(to_send); + return false; +} + +static int default_fwd_hook(int bus_num, int addr) { + UNUSED(bus_num); + UNUSED(addr); + return -1; +} + +const safety_hooks nooutput_hooks = { + .init = nooutput_init, + .rx = default_rx_hook, + .tx = nooutput_tx_hook, + .fwd = default_fwd_hook, +}; + +// *** all output safety mode *** + +// Enables passthrough mode where relay is open and bus 0 gets forwarded to bus 2 and vice versa +const uint16_t ALLOUTPUT_PARAM_PASSTHROUGH = 1; +bool alloutput_passthrough = false; + +static safety_config alloutput_init(uint16_t param) { + controls_allowed = true; + alloutput_passthrough = GET_FLAG(param, ALLOUTPUT_PARAM_PASSTHROUGH); + return (safety_config){NULL, 0, NULL, 0}; +} + +static bool alloutput_tx_hook(const CANPacket_t *to_send) { + UNUSED(to_send); + return true; +} + +static int alloutput_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + UNUSED(addr); + + if (alloutput_passthrough) { + if (bus_num == 0) { + bus_fwd = 2; + } + if (bus_num == 2) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +const safety_hooks alloutput_hooks = { + .init = alloutput_init, + .rx = default_rx_hook, + .tx = alloutput_tx_hook, + .fwd = alloutput_fwd_hook, +}; diff --git a/panda/board/safety/safety_elm327.h b/panda/board/safety/safety_elm327.h new file mode 100644 index 0000000..954efca --- /dev/null +++ b/panda/board/safety/safety_elm327.h @@ -0,0 +1,37 @@ +const int GM_CAMERA_DIAG_ADDR = 0x24B; + +static bool elm327_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + int len = GET_LEN(to_send); + + // All ISO 15765-4 messages must be 8 bytes long + if (len != 8) { + tx = false; + } + + // Check valid 29 bit send addresses for ISO 15765-4 + // Check valid 11 bit send addresses for ISO 15765-4 + if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) && + ((addr & 0x1FFFFF00) != 0x600) && ((addr & 0x1FFFFF00) != 0x700) && + (addr != GM_CAMERA_DIAG_ADDR)) { + tx = false; + } + + // GM camera uses non-standard diagnostic address, this has no control message address collisions + if ((addr == GM_CAMERA_DIAG_ADDR) && (len == 8)) { + // Only allow known frame types for ISO 15765-2 + if ((GET_BYTE(to_send, 0) & 0xF0U) > 0x30U) { + tx = false; + } + } + return tx; +} + +// If current_board->has_obd and safety_param == 0, bus 1 is multiplexed to the OBD-II port +const safety_hooks elm327_hooks = { + .init = nooutput_init, + .rx = default_rx_hook, + .tx = elm327_tx_hook, + .fwd = default_fwd_hook, +}; diff --git a/panda/board/safety/safety_ford.h b/panda/board/safety/safety_ford.h new file mode 100644 index 0000000..e17cdf3 --- /dev/null +++ b/panda/board/safety/safety_ford.h @@ -0,0 +1,424 @@ +// Safety-relevant CAN messages for Ford vehicles. +#define FORD_EngBrakeData 0x165 // RX from PCM, for driver brake pedal and cruise state +#define FORD_EngVehicleSpThrottle 0x204 // RX from PCM, for driver throttle input +#define FORD_DesiredTorqBrk 0x213 // RX from ABS, for standstill state +#define FORD_BrakeSysFeatures 0x415 // RX from ABS, for vehicle speed +#define FORD_EngVehicleSpThrottle2 0x202 // RX from PCM, for second vehicle speed +#define FORD_Yaw_Data_FD1 0x91 // RX from RCM, for yaw rate +#define FORD_Steering_Data_FD1 0x083 // TX by OP, various driver switches and LKAS/CC buttons +#define FORD_ACCDATA 0x186 // TX by OP, ACC controls +#define FORD_ACCDATA_3 0x18A // TX by OP, ACC/TJA user interface +#define FORD_Lane_Assist_Data1 0x3CA // TX by OP, Lane Keep Assist +#define FORD_LateralMotionControl 0x3D3 // TX by OP, Lateral Control message +#define FORD_LateralMotionControl2 0x3D6 // TX by OP, alternate Lateral Control message +#define FORD_IPMA_Data 0x3D8 // TX by OP, IPMA and LKAS user interface + +// CAN bus numbers. +#define FORD_MAIN_BUS 0U +#define FORD_CAM_BUS 2U + +const CanMsg FORD_STOCK_TX_MSGS[] = { + {FORD_Steering_Data_FD1, 0, 8}, + {FORD_Steering_Data_FD1, 2, 8}, + {FORD_ACCDATA_3, 0, 8}, + {FORD_Lane_Assist_Data1, 0, 8}, + {FORD_LateralMotionControl, 0, 8}, + {FORD_IPMA_Data, 0, 8}, +}; + +const CanMsg FORD_LONG_TX_MSGS[] = { + {FORD_Steering_Data_FD1, 0, 8}, + {FORD_Steering_Data_FD1, 2, 8}, + {FORD_ACCDATA, 0, 8}, + {FORD_ACCDATA_3, 0, 8}, + {FORD_Lane_Assist_Data1, 0, 8}, + {FORD_LateralMotionControl, 0, 8}, + {FORD_IPMA_Data, 0, 8}, +}; + +const CanMsg FORD_CANFD_STOCK_TX_MSGS[] = { + {FORD_Steering_Data_FD1, 0, 8}, + {FORD_Steering_Data_FD1, 2, 8}, + {FORD_ACCDATA_3, 0, 8}, + {FORD_Lane_Assist_Data1, 0, 8}, + {FORD_LateralMotionControl2, 0, 8}, + {FORD_IPMA_Data, 0, 8}, +}; + +const CanMsg FORD_CANFD_LONG_TX_MSGS[] = { + {FORD_Steering_Data_FD1, 0, 8}, + {FORD_Steering_Data_FD1, 2, 8}, + {FORD_ACCDATA, 0, 8}, + {FORD_ACCDATA_3, 0, 8}, + {FORD_Lane_Assist_Data1, 0, 8}, + {FORD_LateralMotionControl2, 0, 8}, + {FORD_IPMA_Data, 0, 8}, +}; + +// warning: quality flags are not yet checked in openpilot's CAN parser, +// this may be the cause of blocked messages +RxCheck ford_rx_checks[] = { + {.msg = {{FORD_BrakeSysFeatures, 0, 8, .check_checksum = true, .max_counter = 15U, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, + // FORD_EngVehicleSpThrottle2 has a counter that either randomly skips or by 2, likely ECU bug + // Some hybrid models also experience a bug where this checksum mismatches for one or two frames under heavy acceleration with ACC + // It has been confirmed that the Bronco Sport's camera only disallows ACC for bad quality flags, not counters or checksums, so we match that + {.msg = {{FORD_EngVehicleSpThrottle2, 0, 8, .check_checksum = false, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{FORD_Yaw_Data_FD1, 0, 8, .check_checksum = true, .max_counter = 255U, .quality_flag=true, .frequency = 100U}, { 0 }, { 0 }}}, + // These messages have no counter or checksum + {.msg = {{FORD_EngBrakeData, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{FORD_EngVehicleSpThrottle, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{FORD_DesiredTorqBrk, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +static uint8_t ford_get_counter(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t cnt = 0; + if (addr == FORD_BrakeSysFeatures) { + // Signal: VehVActlBrk_No_Cnt + cnt = (GET_BYTE(to_push, 2) >> 2) & 0xFU; + } else if (addr == FORD_Yaw_Data_FD1) { + // Signal: VehRollYaw_No_Cnt + cnt = GET_BYTE(to_push, 5); + } else { + } + return cnt; +} + +static uint32_t ford_get_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t chksum = 0; + if (addr == FORD_BrakeSysFeatures) { + // Signal: VehVActlBrk_No_Cs + chksum = GET_BYTE(to_push, 3); + } else if (addr == FORD_Yaw_Data_FD1) { + // Signal: VehRollYawW_No_Cs + chksum = GET_BYTE(to_push, 4); + } else { + } + return chksum; +} + +static uint32_t ford_compute_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t chksum = 0; + if (addr == FORD_BrakeSysFeatures) { + chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // Veh_V_ActlBrk + chksum += GET_BYTE(to_push, 2) >> 6; // VehVActlBrk_D_Qf + chksum += (GET_BYTE(to_push, 2) >> 2) & 0xFU; // VehVActlBrk_No_Cnt + chksum = 0xFFU - chksum; + } else if (addr == FORD_Yaw_Data_FD1) { + chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // VehRol_W_Actl + chksum += GET_BYTE(to_push, 2) + GET_BYTE(to_push, 3); // VehYaw_W_Actl + chksum += GET_BYTE(to_push, 5); // VehRollYaw_No_Cnt + chksum += GET_BYTE(to_push, 6) >> 6; // VehRolWActl_D_Qf + chksum += (GET_BYTE(to_push, 6) >> 4) & 0x3U; // VehYawWActl_D_Qf + chksum = 0xFFU - chksum; + } else { + } + + return chksum; +} + +static bool ford_get_quality_flag_valid(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + bool valid = false; + if (addr == FORD_BrakeSysFeatures) { + valid = (GET_BYTE(to_push, 2) >> 6) == 0x3U; // VehVActlBrk_D_Qf + } else if (addr == FORD_EngVehicleSpThrottle2) { + valid = ((GET_BYTE(to_push, 4) >> 5) & 0x3U) == 0x3U; // VehVActlEng_D_Qf + } else if (addr == FORD_Yaw_Data_FD1) { + valid = ((GET_BYTE(to_push, 6) >> 4) & 0x3U) == 0x3U; // VehYawWActl_D_Qf + } else { + } + return valid; +} + +const uint16_t FORD_PARAM_LONGITUDINAL = 1; +const uint16_t FORD_PARAM_CANFD = 2; + +bool ford_longitudinal = false; +bool ford_canfd = false; + +const LongitudinalLimits FORD_LONG_LIMITS = { + // acceleration cmd limits (used for brakes) + // Signal: AccBrkTot_A_Rq + .max_accel = 5641, // 1.9999 m/s^s + .min_accel = 4231, // -3.4991 m/s^2 + .inactive_accel = 5128, // -0.0008 m/s^2 + + // gas cmd limits + // Signal: AccPrpl_A_Rq & AccPrpl_A_Pred + .max_gas = 700, // 2.0 m/s^2 + .min_gas = 450, // -0.5 m/s^2 + .inactive_gas = 0, // -5.0 m/s^2 +}; + +#define FORD_INACTIVE_CURVATURE 1000U +#define FORD_INACTIVE_CURVATURE_RATE 4096U +#define FORD_INACTIVE_PATH_OFFSET 512U +#define FORD_INACTIVE_PATH_ANGLE 1000U + +#define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U + +#define FORD_MAX_SPEED_DELTA 2.0 // m/s + +static bool ford_lkas_msg_check(int addr) { + return (addr == FORD_ACCDATA_3) + || (addr == FORD_Lane_Assist_Data1) + || (addr == FORD_LateralMotionControl) + || (addr == FORD_LateralMotionControl2) + || (addr == FORD_IPMA_Data); +} + +// Curvature rate limits +const SteeringLimits FORD_STEERING_LIMITS = { + .max_steer = 1000, + .angle_deg_to_can = 50000, // 1 / (2e-5) rad to can + .max_angle_error = 100, // 0.002 * FORD_STEERING_LIMITS.angle_deg_to_can + .angle_rate_up_lookup = { + {5., 25., 25.}, + {0.0002, 0.0001, 0.0001} + }, + .angle_rate_down_lookup = { + {5., 25., 25.}, + {0.000225, 0.00015, 0.00015} + }, + + // no blending at low speed due to lack of torque wind-up and inaccurate current curvature + .angle_error_min_speed = 10.0, // m/s + + .enforce_angle_error = true, + .inactive_angle_is_zero = true, +}; + +static void ford_rx_hook(const CANPacket_t *to_push) { + if (GET_BUS(to_push) == FORD_MAIN_BUS) { + int addr = GET_ADDR(to_push); + + // Update in motion state from standstill signal + if (addr == FORD_DesiredTorqBrk) { + // Signal: VehStop_D_Stat + vehicle_moving = ((GET_BYTE(to_push, 3) >> 3) & 0x3U) != 1U; + } + + // Update vehicle speed + if (addr == FORD_BrakeSysFeatures) { + // Signal: Veh_V_ActlBrk + UPDATE_VEHICLE_SPEED(((GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1)) * 0.01 / 3.6); + } + + // Check vehicle speed against a second source + if (addr == FORD_EngVehicleSpThrottle2) { + // Disable controls if speeds from ABS and PCM ECUs are too far apart. + // Signal: Veh_V_ActlEng + float filtered_pcm_speed = ((GET_BYTE(to_push, 6) << 8) | GET_BYTE(to_push, 7)) * 0.01 / 3.6; + bool is_invalid_speed = ABS(filtered_pcm_speed - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) > FORD_MAX_SPEED_DELTA; + if (is_invalid_speed) { + controls_allowed = false; + } + } + + // Update vehicle yaw rate + if (addr == FORD_Yaw_Data_FD1) { + // Signal: VehYaw_W_Actl + float ford_yaw_rate = (((GET_BYTE(to_push, 2) << 8U) | GET_BYTE(to_push, 3)) * 0.0002) - 6.5; + float current_curvature = ford_yaw_rate / MAX(vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR, 0.1); + // convert current curvature into units on CAN for comparison with desired curvature + update_sample(&angle_meas, ROUND(current_curvature * FORD_STEERING_LIMITS.angle_deg_to_can)); + } + + // Update gas pedal + if (addr == FORD_EngVehicleSpThrottle) { + // Pedal position: (0.1 * val) in percent + // Signal: ApedPos_Pc_ActlArb + gas_pressed = (((GET_BYTE(to_push, 0) & 0x03U) << 8) | GET_BYTE(to_push, 1)) > 0U; + } + + // Update brake pedal and cruise state + if (addr == FORD_EngBrakeData) { + // Signal: BpedDrvAppl_D_Actl + brake_pressed = ((GET_BYTE(to_push, 0) >> 4) & 0x3U) == 2U; + + // Signal: CcStat_D_Actl + unsigned int cruise_state = GET_BYTE(to_push, 1) & 0x07U; + acc_main_on = (cruise_state == 3U) ||(cruise_state == 4U) || (cruise_state == 5U); + bool cruise_engaged = (cruise_state == 4U) || (cruise_state == 5U); + pcm_cruise_check(cruise_engaged); + } + + // If steering controls messages are received on the destination bus, it's an indication + // that the relay might be malfunctioning. + bool stock_ecu_detected = ford_lkas_msg_check(addr); + if (ford_longitudinal) { + stock_ecu_detected = stock_ecu_detected || (addr == FORD_ACCDATA); + } + generic_rx_checks(stock_ecu_detected); + } + +} + +static bool ford_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + + int addr = GET_ADDR(to_send); + + // Safety check for ACCDATA accel and brake requests + if (addr == FORD_ACCDATA) { + // Signal: AccPrpl_A_Rq + int gas = ((GET_BYTE(to_send, 6) & 0x3U) << 8) | GET_BYTE(to_send, 7); + // Signal: AccPrpl_A_Pred + int gas_pred = ((GET_BYTE(to_send, 2) & 0x3U) << 8) | GET_BYTE(to_send, 3); + // Signal: AccBrkTot_A_Rq + int accel = ((GET_BYTE(to_send, 0) & 0x1FU) << 8) | GET_BYTE(to_send, 1); + // Signal: CmbbDeny_B_Actl + bool cmbb_deny = GET_BIT(to_send, 37U); + + bool violation = false; + violation |= longitudinal_accel_checks(accel, FORD_LONG_LIMITS); + violation |= longitudinal_gas_checks(gas, FORD_LONG_LIMITS); + violation |= longitudinal_gas_checks(gas_pred, FORD_LONG_LIMITS); + + // Safety check for stock AEB + violation |= cmbb_deny; // do not prevent stock AEB actuation + + if (violation) { + tx = false; + } + } + + // Safety check for Steering_Data_FD1 button signals + // Note: Many other signals in this message are not relevant to safety (e.g. blinkers, wiper switches, high beam) + // which we passthru in OP. + if (addr == FORD_Steering_Data_FD1) { + // Violation if resume button is pressed while controls not allowed, or + // if cancel button is pressed when cruise isn't engaged. + bool violation = false; + violation |= GET_BIT(to_send, 8U) && !cruise_engaged_prev; // Signal: CcAslButtnCnclPress (cancel) + violation |= GET_BIT(to_send, 25U) && !controls_allowed; // Signal: CcAsllButtnResPress (resume) + + if (violation) { + tx = false; + } + } + + // Safety check for Lane_Assist_Data1 action + if (addr == FORD_Lane_Assist_Data1) { + // Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid). + // This message must be sent for Lane Centering to work, and can include + // values such as the steering angle or lane curvature for debugging, + // but the action (LkaActvStats_D2_Req) must be set to zero. + unsigned int action = GET_BYTE(to_send, 0) >> 5; + if (action != 0U) { + tx = false; + } + } + + // Safety check for LateralMotionControl action + if (addr == FORD_LateralMotionControl) { + // Signal: LatCtl_D_Rq + bool steer_control_enabled = ((GET_BYTE(to_send, 4) >> 2) & 0x7U) != 0U; + unsigned int raw_curvature = (GET_BYTE(to_send, 0) << 3) | (GET_BYTE(to_send, 1) >> 5); + unsigned int raw_curvature_rate = ((GET_BYTE(to_send, 1) & 0x1FU) << 8) | GET_BYTE(to_send, 2); + unsigned int raw_path_angle = (GET_BYTE(to_send, 3) << 3) | (GET_BYTE(to_send, 4) >> 5); + unsigned int raw_path_offset = (GET_BYTE(to_send, 5) << 2) | (GET_BYTE(to_send, 6) >> 6); + + // These signals are not yet tested with the current safety limits + bool violation = (raw_curvature_rate != FORD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); + + // Check angle error and steer_control_enabled + int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature + violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); + + if (violation) { + tx = false; + } + } + + // Safety check for LateralMotionControl2 action + if (addr == FORD_LateralMotionControl2) { + // Signal: LatCtl_D2_Rq + bool steer_control_enabled = ((GET_BYTE(to_send, 0) >> 4) & 0x7U) != 0U; + unsigned int raw_curvature = (GET_BYTE(to_send, 2) << 3) | (GET_BYTE(to_send, 3) >> 5); + unsigned int raw_curvature_rate = (GET_BYTE(to_send, 6) << 3) | (GET_BYTE(to_send, 7) >> 5); + unsigned int raw_path_angle = ((GET_BYTE(to_send, 3) & 0x1FU) << 6) | (GET_BYTE(to_send, 4) >> 2); + unsigned int raw_path_offset = ((GET_BYTE(to_send, 4) & 0x3U) << 8) | GET_BYTE(to_send, 5); + + // These signals are not yet tested with the current safety limits + bool violation = (raw_curvature_rate != FORD_CANFD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); + + // Check angle error and steer_control_enabled + int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature + violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); + + if (violation) { + tx = false; + } + } + + return tx; +} + +static int ford_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + switch (bus_num) { + case FORD_MAIN_BUS: { + // Forward all traffic from bus 0 onward + bus_fwd = FORD_CAM_BUS; + break; + } + case FORD_CAM_BUS: { + if (ford_lkas_msg_check(addr)) { + // Block stock LKAS and UI messages + bus_fwd = -1; + } else if (ford_longitudinal && (addr == FORD_ACCDATA)) { + // Block stock ACC message + bus_fwd = -1; + } else { + // Forward remaining traffic + bus_fwd = FORD_MAIN_BUS; + } + break; + } + default: { + // No other buses should be in use; fallback to do-not-forward + bus_fwd = -1; + break; + } + } + + return bus_fwd; +} + +static safety_config ford_init(uint16_t param) { + UNUSED(param); +#ifdef ALLOW_DEBUG + ford_longitudinal = GET_FLAG(param, FORD_PARAM_LONGITUDINAL); + ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD); +#endif + + safety_config ret; + if (ford_canfd) { + ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS); + } else { + ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(ford_rx_checks, FORD_STOCK_TX_MSGS); + } + return ret; +} + +const safety_hooks ford_hooks = { + .init = ford_init, + .rx = ford_rx_hook, + .tx = ford_tx_hook, + .fwd = ford_fwd_hook, + .get_counter = ford_get_counter, + .get_checksum = ford_get_checksum, + .compute_checksum = ford_compute_checksum, + .get_quality_flag_valid = ford_get_quality_flag_valid, +}; diff --git a/panda/board/safety/safety_gm.h b/panda/board/safety/safety_gm.h new file mode 100644 index 0000000..89a9a7f --- /dev/null +++ b/panda/board/safety/safety_gm.h @@ -0,0 +1,353 @@ +const SteeringLimits GM_STEERING_LIMITS = { + .max_steer = 300, + .max_rate_up = 10, + .max_rate_down = 15, + .driver_torque_allowance = 65, + .driver_torque_factor = 4, + .max_rt_delta = 128, + .max_rt_interval = 250000, + .type = TorqueDriverLimited, +}; + +const LongitudinalLimits GM_ASCM_LONG_LIMITS = { + .max_gas = 3072, + .min_gas = 1404, + .inactive_gas = 1404, + .max_brake = 400, +}; + +const LongitudinalLimits GM_ASCM_LONG_LIMITS_SPORT = { + .max_gas = 8191, + .min_gas = 5500, + .inactive_gas = 5500, + .max_brake = 400, +}; + +const LongitudinalLimits GM_CAM_LONG_LIMITS = { + .max_gas = 3400, + .min_gas = 1514, + .inactive_gas = 1554, + .max_brake = 400, +}; + +const LongitudinalLimits GM_CAM_LONG_LIMITS_SPORT = { + .max_gas = 8848, + .min_gas = 5610, + .inactive_gas = 5650, + .max_brake = 400, +}; + +const LongitudinalLimits *gm_long_limits; + +const int GM_STANDSTILL_THRSLD = 10; // 0.311kph + +// panda interceptor threshold needs to be equivalent to openpilot threshold to avoid controls mismatches +// If thresholds are mismatched then it is possible for panda to see the gas fall and rise while openpilot is in the pre-enabled state +const int GM_GAS_INTERCEPTOR_THRESHOLD = 515; // (610 + 306.25) / 2 ratio between offset and gain from dbc file +#define GM_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + (GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2U) // avg between 2 tracks + +const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, // pt bus + {0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus + {0x315, 2, 5}, // ch bus + {0x104c006c, 3, 3}, {0x10400060, 3, 5}}; // gmlan + +const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, // pt bus + {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus + +const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, // pt bus + {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus + +const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus + {0x184, 2, 8}}; // camera bus + +const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, // pt bus + {0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus + +// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. +RxCheck gm_rx_checks[] = { + {.msg = {{0x184, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{0x34A, 0, 5, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{0x1E1, 0, 7, .frequency = 10U}, // Non-SDGM Car + {0x1E1, 2, 7, .frequency = 100000U}}}, // SDGM Car + {.msg = {{0xF1, 0, 6, .frequency = 10U}, // Non-SDGM Car + {0xF1, 2, 6, .frequency = 100000U}}}, // SDGM Car + {.msg = {{0x1C4, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{0xC9, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, +}; + +const uint16_t GM_PARAM_HW_CAM = 1; +const uint16_t GM_PARAM_HW_CAM_LONG = 2; +const uint16_t GM_PARAM_HW_SDGM = 4; +const uint16_t GM_PARAM_CC_LONG = 8; +const uint16_t GM_PARAM_HW_ASCM_LONG = 16; +const uint16_t GM_PARAM_NO_CAMERA = 32; +const uint16_t GM_PARAM_NO_ACC = 64; +const uint16_t GM_PARAM_PEDAL_LONG = 128; // TODO: this can be inferred +const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 256; + +enum { + GM_BTN_UNPRESS = 1, + GM_BTN_RESUME = 2, + GM_BTN_SET = 3, + GM_BTN_CANCEL = 6, +}; + +enum {GM_ASCM, GM_CAM, GM_SDGM} gm_hw = GM_ASCM; +bool gm_cam_long = false; +bool gm_pcm_cruise = false; +bool gm_cc_long = false; +bool gm_has_acc = true; +bool gm_pedal_long = false; +bool gm_skip_relay_check = false; +bool gm_force_ascm = false; + +static void handle_gm_wheel_buttons(const CANPacket_t *to_push) { + int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; + + // enter controls on falling edge of set or rising edge of resume (avoids fault) + bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET); + bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME); + if (set || res) { + controls_allowed = true; + } + + // exit controls on cancel press + if (button == GM_BTN_CANCEL) { + controls_allowed = false; + } + + cruise_button_prev = button; +} + +static void gm_rx_hook(const CANPacket_t *to_push) { + if ((GET_BUS(to_push) == 2U) && (GET_ADDR(to_push) == 0x1E1) && (gm_hw == GM_SDGM)) { + // SDGM buttons are on bus 2 + handle_gm_wheel_buttons(to_push); + } + if (GET_BUS(to_push) == 0U) { + int addr = GET_ADDR(to_push); + + if (addr == 0x184) { + int torque_driver_new = ((GET_BYTE(to_push, 6) & 0x7U) << 8) | GET_BYTE(to_push, 7); + torque_driver_new = to_signed(torque_driver_new, 11); + // update array of samples + update_sample(&torque_driver, torque_driver_new); + } + + // sample rear wheel speeds + if (addr == 0x34A) { + int left_rear_speed = (GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1); + int right_rear_speed = (GET_BYTE(to_push, 2) << 8) | GET_BYTE(to_push, 3); + vehicle_moving = (left_rear_speed > GM_STANDSTILL_THRSLD) || (right_rear_speed > GM_STANDSTILL_THRSLD); + } + + // ACC steering wheel buttons (GM_CAM and GM_SDGM are tied to the PCM) + if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long) && (gm_hw != GM_SDGM)) { + handle_gm_wheel_buttons(to_push); + } + + // Reference for brake pressed signals: + // https://github.com/commaai/openpilot/blob/master/selfdrive/car/gm/carstate.py + if ((addr == 0xBE) && (gm_hw == GM_ASCM)) { + brake_pressed = GET_BYTE(to_push, 1) >= 8U; + } + + if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) { + brake_pressed = GET_BIT(to_push, 40U) != 0U; + } + + if (addr == 0xC9) { + acc_main_on = GET_BIT(to_push, 29U) != 0U; + } + + if (addr == 0x1C4) { + if (!enable_gas_interceptor) { + gas_pressed = GET_BYTE(to_push, 5) != 0U; + } + + // enter controls on rising edge of ACC, exit controls when ACC off + if (gm_pcm_cruise && gm_has_acc) { + bool cruise_engaged = (GET_BYTE(to_push, 1) >> 5) != 0U; + pcm_cruise_check(cruise_engaged); + } + } + + // Cruise check for CC only cars + if ((addr == 0x3D1) && !gm_has_acc) { + bool cruise_engaged = (GET_BYTE(to_push, 4) >> 7) != 0U; + if (gm_cc_long) { + pcm_cruise_check(cruise_engaged); + } else { + cruise_engaged_prev = cruise_engaged; + } + } + + if (addr == 0xBD) { + regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U; + } + + // Pedal Interceptor + if ((addr == 0x201) && enable_gas_interceptor) { + int gas_interceptor = GM_GET_INTERCEPTOR(to_push); + gas_pressed = gas_interceptor > GM_GAS_INTERCEPTOR_THRESHOLD; + gas_interceptor_prev = gas_interceptor; + } + + bool stock_ecu_detected = (addr == 0x180); // ASCMLKASteeringCmd + + // Check ASCMGasRegenCmd only if we're blocking it + if (!gm_pcm_cruise && !gm_pedal_long && (addr == 0x2CB)) { + stock_ecu_detected = true; + } + generic_rx_checks(stock_ecu_detected); + } +} + +static bool gm_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + + // BRAKE: safety check + if (addr == 0x315) { + int brake = ((GET_BYTE(to_send, 0) & 0xFU) << 8) + GET_BYTE(to_send, 1); + brake = (0x1000 - brake) & 0xFFF; + if (longitudinal_brake_checks(brake, *gm_long_limits)) { + tx = false; + } + } + + // LKA STEER: safety check + if (addr == 0x180) { + int desired_torque = ((GET_BYTE(to_send, 0) & 0x7U) << 8) + GET_BYTE(to_send, 1); + desired_torque = to_signed(desired_torque, 11); + + bool steer_req = GET_BIT(to_send, 3U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, GM_STEERING_LIMITS)) { + tx = false; + } + } + + // GAS: safety check (interceptor) + if (addr == 0x200) { + if (longitudinal_interceptor_checks(to_send)) { + tx = 0; + } + } + + // GAS/REGEN: safety check + if (addr == 0x2CB) { + bool apply = GET_BIT(to_send, 0U); + int gas_regen = ((GET_BYTE(to_send, 1) & 0x1U) << 13) + ((GET_BYTE(to_send, 2) & 0xFFU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); + + bool violation = false; + // Allow apply bit in pre-enabled and overriding states + violation |= !controls_allowed && apply; + violation |= longitudinal_gas_checks(gas_regen, *gm_long_limits); + + if (violation) { + tx = false; + } + } + + // BUTTONS: used for resume spamming and cruise cancellation with stock longitudinal + if ((addr == 0x1E1) && (gm_pcm_cruise || gm_pedal_long || gm_cc_long)) { + int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; + + bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev; + // For standard CC, allow spamming of SET / RESUME + if (gm_cc_long) { + allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS); + } + + if (!allowed_btn) { + tx = false; + } + } + + return tx; +} + +static int gm_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) { + if (bus_num == 0) { + // block PSCMStatus; forwarded through openpilot to hide an alert from the camera + bool is_pscm_msg = (addr == 0x184); + if (!is_pscm_msg) { + bus_fwd = 2; + } + } + + if (bus_num == 2) { + // block lkas message and acc messages if gm_cam_long, forward all others + bool is_lkas_msg = (addr == 0x180); + bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); + bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); + if (!block_msg) { + bus_fwd = 0; + } + } + } + + return bus_fwd; +} + +static safety_config gm_init(uint16_t param) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + if GET_FLAG(param, GM_PARAM_HW_CAM) { + gm_hw = GM_CAM; + } else if GET_FLAG(param, GM_PARAM_HW_SDGM) { + gm_hw = GM_SDGM; + } else { + gm_hw = GM_ASCM; + } + + gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG); + + if (gm_hw == GM_ASCM || gm_force_ascm) { + if (sport_mode) { + gm_long_limits = &GM_ASCM_LONG_LIMITS_SPORT; + } else { + gm_long_limits = &GM_ASCM_LONG_LIMITS; + } + } else if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) { + if (sport_mode) { + gm_long_limits = &GM_CAM_LONG_LIMITS_SPORT; + } else { + gm_long_limits = &GM_CAM_LONG_LIMITS; + } + } else { + } + + gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); + gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); + gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long; + gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM); + gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA); + gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); + enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR); + + safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS); + if (gm_hw == GM_CAM) { + if (gm_cc_long) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CC_LONG_TX_MSGS); + } else if (gm_cam_long) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_LONG_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); + } + } else if (gm_hw == GM_SDGM) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS); + } + return ret; +} + +const safety_hooks gm_hooks = { + .init = gm_init, + .rx = gm_rx_hook, + .tx = gm_tx_hook, + .fwd = gm_fwd_hook, +}; diff --git a/panda/board/safety/safety_honda.h b/panda/board/safety/safety_honda.h new file mode 100644 index 0000000..9af384b --- /dev/null +++ b/panda/board/safety/safety_honda.h @@ -0,0 +1,526 @@ +const CanMsg HONDA_N_TX_MSGS[] = {{0xE4, 0, 5}, {0x194, 0, 4}, {0x1FA, 0, 8}, {0x30C, 0, 8}, {0x33D, 0, 5}}; +const CanMsg HONDA_N_INTERCEPTOR_TX_MSGS[] = {{0xE4, 0, 5}, {0x194, 0, 4}, {0x1FA, 0, 8}, {0x200, 0, 6}, {0x30C, 0, 8}, {0x33D, 0, 5}}; +const CanMsg HONDA_BOSCH_TX_MSGS[] = {{0xE4, 0, 5}, {0xE5, 0, 8}, {0x296, 1, 4}, {0x33D, 0, 5}, {0x33DA, 0, 5}, {0x33DB, 0, 8}}; // Bosch +const CanMsg HONDA_BOSCH_LONG_TX_MSGS[] = {{0xE4, 1, 5}, {0x1DF, 1, 8}, {0x1EF, 1, 8}, {0x1FA, 1, 8}, {0x30C, 1, 8}, {0x33D, 1, 5}, {0x33DA, 1, 5}, {0x33DB, 1, 8}, {0x39F, 1, 8}, {0x18DAB0F1, 1, 8}}; // Bosch w/ gas and brakes +const CanMsg HONDA_RADARLESS_TX_MSGS[] = {{0xE4, 0, 5}, {0x296, 2, 4}, {0x33D, 0, 8}}; // Bosch radarless +const CanMsg HONDA_RADARLESS_LONG_TX_MSGS[] = {{0xE4, 0, 5}, {0x33D, 0, 8}, {0x1C8, 0, 8}, {0x30C, 0, 8}}; // Bosch radarless w/ gas and brakes + +// panda interceptor threshold needs to be equivalent to openpilot threshold to avoid controls mismatches +// If thresholds are mismatched then it is possible for panda to see the gas fall and rise while openpilot is in the pre-enabled state +// Threshold calculated from DBC gains: round(((83.3 / 0.253984064) + (83.3 / 0.126992032)) / 2) = 492 +const int HONDA_GAS_INTERCEPTOR_THRESHOLD = 492; +#define HONDA_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + (GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2U) // avg between 2 tracks + +const LongitudinalLimits HONDA_BOSCH_LONG_LIMITS = { + .max_accel = 200, // accel is used for brakes + .min_accel = -350, + + .max_gas = 2000, + .inactive_gas = -30000, +}; + +const LongitudinalLimits HONDA_BOSCH_LONG_LIMITS_SPORT = { + .max_accel = 400, // accel is used for brakes + .min_accel = -350, + + .max_gas = 2000, + .inactive_gas = -30000, +}; + +const LongitudinalLimits HONDA_NIDEC_LONG_LIMITS = { + .max_gas = 198, // 0xc6 + .max_brake = 255, + + .inactive_speed = 0, +}; + +// All common address checks except SCM_BUTTONS which isn't on one Nidec safety configuration +#define HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(pt_bus) \ + {.msg = {{0x1A6, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 25U}, /* SCM_BUTTONS */ \ + {0x296, (pt_bus), 4, .check_checksum = true, .max_counter = 3U, .frequency = 25U}, { 0 }}}, \ + {.msg = {{0x158, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, { 0 }, { 0 }}}, /* ENGINE_DATA */ \ + {.msg = {{0x17C, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, { 0 }, { 0 }}}, /* POWERTRAIN_DATA */ \ + +#define HONDA_COMMON_RX_CHECKS(pt_bus) \ + HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(pt_bus) \ + {.msg = {{0x326, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 10U}, { 0 }, { 0 }}}, /* SCM_FEEDBACK */ \ + +// Alternate brake message is used on some Honda Bosch, and Honda Bosch radarless (where PT bus is 0) +#define HONDA_ALT_BRAKE_ADDR_CHECK(pt_bus) \ + {.msg = {{0x1BE, (pt_bus), 3, .check_checksum = true, .max_counter = 3U, .frequency = 50U}, { 0 }, { 0 }}}, /* BRAKE_MODULE */ \ + + +// Nidec and bosch radarless has the powertrain bus on bus 0 +RxCheck honda_common_rx_checks[] = { + HONDA_COMMON_RX_CHECKS(0) +}; + +RxCheck honda_common_interceptor_rx_checks[] = { + HONDA_COMMON_RX_CHECKS(0) + {.msg = {{0x201, 0, 6, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +RxCheck honda_common_alt_brake_rx_checks[] = { + HONDA_COMMON_RX_CHECKS(0) + HONDA_ALT_BRAKE_ADDR_CHECK(0) +}; + +// For Nidecs with main on signal on an alternate msg (missing 0x326) +RxCheck honda_nidec_alt_rx_checks[] = { + HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(0) +}; + +RxCheck honda_nidec_alt_interceptor_rx_checks[] = { + HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(0) + {.msg = {{0x201, 0, 6, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +// Bosch has pt on bus 1, verified 0x1A6 does not exist +RxCheck honda_bosch_rx_checks[] = { + HONDA_COMMON_RX_CHECKS(1) +}; + +RxCheck honda_bosch_alt_brake_rx_checks[] = { + HONDA_COMMON_RX_CHECKS(1) + HONDA_ALT_BRAKE_ADDR_CHECK(1) +}; + +const uint16_t HONDA_PARAM_ALT_BRAKE = 1; +const uint16_t HONDA_PARAM_BOSCH_LONG = 2; +const uint16_t HONDA_PARAM_NIDEC_ALT = 4; +const uint16_t HONDA_PARAM_RADARLESS = 8; +const uint16_t HONDA_PARAM_GAS_INTERCEPTOR = 16; +const uint16_t HONDA_PARAM_CLARITY = 32; + +enum { + HONDA_BTN_NONE = 0, + HONDA_BTN_MAIN = 1, + HONDA_BTN_CANCEL = 2, + HONDA_BTN_SET = 3, + HONDA_BTN_RESUME = 4, +}; + +int honda_brake = 0; +bool honda_brake_switch_prev = false; +bool honda_alt_brake_msg = false; +bool honda_fwd_brake = false; +bool honda_bosch_long = false; +bool honda_bosch_radarless = false; +bool honda_clarity_brake_msg = false; +enum {HONDA_NIDEC, HONDA_BOSCH} honda_hw = HONDA_NIDEC; + + +int honda_get_pt_bus(void) { + return ((honda_hw == HONDA_BOSCH) && !honda_bosch_radarless) ? 1 : 0; +} + +static uint32_t honda_get_checksum(const CANPacket_t *to_push) { + int checksum_byte = GET_LEN(to_push) - 1U; + return (uint8_t)(GET_BYTE(to_push, checksum_byte)) & 0xFU; +} + +static uint32_t honda_compute_checksum(const CANPacket_t *to_push) { + int len = GET_LEN(to_push); + uint8_t checksum = 0U; + unsigned int addr = GET_ADDR(to_push); + while (addr > 0U) { + checksum += (uint8_t)(addr & 0xFU); addr >>= 4; + } + for (int j = 0; j < len; j++) { + uint8_t byte = GET_BYTE(to_push, j); + checksum += (uint8_t)(byte & 0xFU) + (byte >> 4U); + if (j == (len - 1)) { + checksum -= (byte & 0xFU); // remove checksum in message + } + } + return (uint8_t)((8U - checksum) & 0xFU); +} + +static uint8_t honda_get_counter(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t cnt = 0U; + if (addr == 0x201) { + // Signal: COUNTER_PEDAL + cnt = GET_BYTE(to_push, 4) & 0x0FU; + } else { + int counter_byte = GET_LEN(to_push) - 1U; + cnt = (GET_BYTE(to_push, counter_byte) >> 4U) & 0x3U; + } + return cnt; +} + +static void honda_rx_hook(const CANPacket_t *to_push) { + const bool pcm_cruise = ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) || \ + ((honda_hw == HONDA_NIDEC) && !enable_gas_interceptor); + int pt_bus = honda_get_pt_bus(); + + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + int bus = GET_BUS(to_push); + + // sample speed + if (addr == 0x158) { + // first 2 bytes + vehicle_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1); + } + + // check ACC main state + // 0x326 for all Bosch and some Nidec, 0x1A6 for some Nidec + if ((addr == 0x326) || (addr == 0x1A6)) { + acc_main_on = GET_BIT(to_push, ((addr == 0x326) ? 28U : 47U)); + if (!acc_main_on) { + controls_allowed = false; + } + } + + // enter controls when PCM enters cruise state + if (pcm_cruise && (addr == 0x17C)) { + const bool cruise_engaged = GET_BIT(to_push, 38U); + // engage on rising edge + if (cruise_engaged && !cruise_engaged_prev) { + controls_allowed = true; + } + + // Since some Nidec cars can brake down to 0 after the PCM disengages, + // we don't disengage when the PCM does. + if (!cruise_engaged && (honda_hw != HONDA_NIDEC)) { + controls_allowed = false; + } + cruise_engaged_prev = cruise_engaged; + } + + // state machine to enter and exit controls for button enabling + // 0x1A6 for the ILX, 0x296 for the Civic Touring + if (((addr == 0x1A6) || (addr == 0x296)) && (bus == pt_bus)) { + int button = (GET_BYTE(to_push, 0) & 0xE0U) >> 5; + + // enter controls on the falling edge of set or resume + bool set = (button != HONDA_BTN_SET) && (cruise_button_prev == HONDA_BTN_SET); + bool res = (button != HONDA_BTN_RESUME) && (cruise_button_prev == HONDA_BTN_RESUME); + if (acc_main_on && !pcm_cruise && (set || res)) { + controls_allowed = true; + } + + // exit controls once main or cancel are pressed + if ((button == HONDA_BTN_MAIN) || (button == HONDA_BTN_CANCEL)) { + controls_allowed = false; + } + cruise_button_prev = button; + } + + // user brake signal on 0x17C reports applied brake from computer brake on accord + // and crv, which prevents the usual brake safety from working correctly. these + // cars have a signal on 0x1BE which only detects user's brake being applied so + // in these cases, this is used instead. + // most hondas: 0x17C + // accord, crv: 0x1BE + if (honda_alt_brake_msg) { + if (addr == 0x1BE) { + brake_pressed = GET_BIT(to_push, 4U); + } + } else { + if (addr == 0x17C) { + // also if brake switch is 1 for two CAN frames, as brake pressed is delayed + const bool brake_switch = GET_BIT(to_push, 32U); + brake_pressed = (GET_BIT(to_push, 53U)) || (brake_switch && honda_brake_switch_prev); + honda_brake_switch_prev = brake_switch; + } + } + + // length check because bosch hardware also uses this id (0x201 w/ len = 8) + if ((addr == 0x201) && (len == 6) && enable_gas_interceptor) { + int gas_interceptor = HONDA_GET_INTERCEPTOR(to_push); + gas_pressed = gas_interceptor > HONDA_GAS_INTERCEPTOR_THRESHOLD; + gas_interceptor_prev = gas_interceptor; + } + + if (!enable_gas_interceptor) { + if (addr == 0x17C) { + gas_pressed = GET_BYTE(to_push, 0) != 0U; + } + } + + // disable stock Honda AEB in alternative experience + if (!(alternative_experience & ALT_EXP_DISABLE_STOCK_AEB)) { + if ((bus == 2) && (addr == 0x1FA)) { + bool honda_stock_aeb = GET_BIT(to_push, 29U); + int honda_stock_brake = (GET_BYTE(to_push, 0) << 2) | (GET_BYTE(to_push, 1) >> 6); + + if (honda_clarity_brake_msg) { + honda_stock_brake = (GET_BYTE(to_push, 6) << 2) + ((GET_BYTE(to_push, 7) >> 6) & 0x3U); + } + // Forward AEB when stock braking is higher than openpilot braking + // only stop forwarding when AEB event is over + if (!honda_stock_aeb) { + honda_fwd_brake = false; + } else if (honda_stock_brake >= honda_brake) { + honda_fwd_brake = true; + } else { + // Leave Honda forward brake as is + } + } + } + + int bus_rdr_car = (honda_hw == HONDA_BOSCH) ? 0 : 2; // radar bus, car side + bool stock_ecu_detected = false; + + // If steering controls messages are received on the destination bus, it's an indication + // that the relay might be malfunctioning + if ((addr == 0xE4) || (addr == 0x194)) { + if (((honda_hw != HONDA_NIDEC) && (bus == bus_rdr_car)) || ((honda_hw == HONDA_NIDEC) && (bus == 0))) { + stock_ecu_detected = true; + } + } + // If Honda Bosch longitudinal mode is selected we need to ensure the radar is turned off + // Verify this by ensuring ACC_CONTROL (0x1DF) is not received on the PT bus + if (honda_bosch_long && !honda_bosch_radarless && (bus == pt_bus) && (addr == 0x1DF)) { + stock_ecu_detected = true; + } + + generic_rx_checks(stock_ecu_detected); + +} + +static bool honda_tx_hook(const CANPacket_t *to_send) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + bool tx = true; + int addr = GET_ADDR(to_send); + int bus = GET_BUS(to_send); + + int bus_pt = honda_get_pt_bus(); + int bus_buttons = (honda_bosch_radarless) ? 2 : bus_pt; // the camera controls ACC on radarless Bosch cars + + // ACC_HUD: safety check (nidec w/o pedal) + if ((addr == 0x30C) && (bus == bus_pt)) { + int pcm_speed = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); + int pcm_gas = GET_BYTE(to_send, 2); + + bool violation = false; + violation |= longitudinal_speed_checks(pcm_speed, HONDA_NIDEC_LONG_LIMITS); + violation |= longitudinal_gas_checks(pcm_gas, HONDA_NIDEC_LONG_LIMITS); + if (violation) { + tx = false; + } + } + + // BRAKE: safety check (nidec) + if ((addr == 0x1FA) && (bus == bus_pt)) { + honda_brake = (GET_BYTE(to_send, 0) << 2) + ((GET_BYTE(to_send, 1) >> 6) & 0x3U); + if (honda_clarity_brake_msg) { + honda_brake = (GET_BYTE(to_send, 6) << 2) + ((GET_BYTE(to_send, 7) >> 6) & 0x3U); + } + if (longitudinal_brake_checks(honda_brake, HONDA_NIDEC_LONG_LIMITS)) { + tx = false; + } + if (honda_fwd_brake) { + tx = false; + } + } + + // BRAKE/GAS: safety check (bosch) + if ((addr == 0x1DF) && (bus == bus_pt)) { + int accel = (GET_BYTE(to_send, 3) << 3) | ((GET_BYTE(to_send, 4) >> 5) & 0x7U); + accel = to_signed(accel, 11); + + int gas = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); + gas = to_signed(gas, 16); + + bool violation = false; + if (sport_mode) { + violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS_SPORT); + violation |= longitudinal_gas_checks(gas, HONDA_BOSCH_LONG_LIMITS_SPORT); + } else { + violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS); + violation |= longitudinal_gas_checks(gas, HONDA_BOSCH_LONG_LIMITS); + } + if (violation) { + tx = false; + } + } + + // ACCEL: safety check (radarless) + if ((addr == 0x1C8) && (bus == bus_pt)) { + int accel = (GET_BYTE(to_send, 0) << 4) | (GET_BYTE(to_send, 1) >> 4); + accel = to_signed(accel, 12); + + bool violation = false; + if (sport_mode) { + violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS_SPORT); + } else { + violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS); + } + if (violation) { + tx = false; + } + } + + // STEER: safety check + if ((addr == 0xE4) || (addr == 0x194)) { + bool aol_allowed = acc_main_on && (alternative_experience & ALT_EXP_DISABLE_DISENGAGE_ON_GAS); + if (!(controls_allowed || aol_allowed)) { + bool steer_applied = GET_BYTE(to_send, 0) | GET_BYTE(to_send, 1); + if (steer_applied) { + tx = false; + } + } + } + + // Bosch supplemental control check + if (addr == 0xE5) { + if ((GET_BYTES(to_send, 0, 4) != 0x10800004U) || ((GET_BYTES(to_send, 4, 4) & 0x00FFFFFFU) != 0x0U)) { + tx = false; + } + } + + // GAS: safety check (interceptor) + if (addr == 0x200) { + if (longitudinal_interceptor_checks(to_send)) { + tx = false; + } + } + + // FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW + // ensuring that only the cancel button press is sent (VAL 2) when controls are off. + // This avoids unintended engagements while still allowing resume spam + if ((addr == 0x296) && !controls_allowed && (bus == bus_buttons)) { + if (((GET_BYTE(to_send, 0) >> 5) & 0x7U) != 2U) { + tx = false; + } + } + + // Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address + if (addr == 0x18DAB0F1) { + if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { + tx = false; + } + } + + return tx; +} + +static safety_config honda_nidec_init(uint16_t param) { + honda_hw = HONDA_NIDEC; + honda_brake = 0; + honda_brake_switch_prev = false; + honda_fwd_brake = false; + honda_alt_brake_msg = false; + honda_bosch_long = false; + honda_bosch_radarless = false; + enable_gas_interceptor = GET_FLAG(param, HONDA_PARAM_GAS_INTERCEPTOR); + honda_clarity_brake_msg = GET_FLAG(param, HONDA_PARAM_CLARITY); + + safety_config ret; + + bool enable_nidec_alt = GET_FLAG(param, HONDA_PARAM_NIDEC_ALT); + if (enable_nidec_alt) { + enable_gas_interceptor ? SET_RX_CHECKS(honda_nidec_alt_interceptor_rx_checks, ret) : \ + SET_RX_CHECKS(honda_nidec_alt_rx_checks, ret); + } else { + enable_gas_interceptor ? SET_RX_CHECKS(honda_common_interceptor_rx_checks, ret) : \ + SET_RX_CHECKS(honda_common_rx_checks, ret); + } + + if (enable_gas_interceptor) { + SET_TX_MSGS(HONDA_N_INTERCEPTOR_TX_MSGS, ret); + } else { + SET_TX_MSGS(HONDA_N_TX_MSGS, ret); + } + return ret; +} + +static safety_config honda_bosch_init(uint16_t param) { + honda_hw = HONDA_BOSCH; + honda_brake_switch_prev = false; + honda_bosch_radarless = GET_FLAG(param, HONDA_PARAM_RADARLESS); + // Checking for alternate brake override from safety parameter + honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE); + + // radar disabled so allow gas/brakes +#ifdef ALLOW_DEBUG + honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); +#endif + + safety_config ret; + if (honda_bosch_radarless && honda_alt_brake_msg) { + SET_RX_CHECKS(honda_common_alt_brake_rx_checks, ret); + } else if (honda_bosch_radarless) { + SET_RX_CHECKS(honda_common_rx_checks, ret); + } else if (honda_alt_brake_msg) { + SET_RX_CHECKS(honda_bosch_alt_brake_rx_checks, ret); + } else { + SET_RX_CHECKS(honda_bosch_rx_checks, ret); + } + + if (honda_bosch_radarless) { + honda_bosch_long ? SET_TX_MSGS(HONDA_RADARLESS_LONG_TX_MSGS, ret) : \ + SET_TX_MSGS(HONDA_RADARLESS_TX_MSGS, ret); + } else { + honda_bosch_long ? SET_TX_MSGS(HONDA_BOSCH_LONG_TX_MSGS, ret) : \ + SET_TX_MSGS(HONDA_BOSCH_TX_MSGS, ret); + } + return ret; +} + +static int honda_nidec_fwd_hook(int bus_num, int addr) { + // fwd from car to camera. also fwd certain msgs from camera to car + // 0xE4 is steering on all cars except CRV and RDX, 0x194 for CRV and RDX, + // 0x1FA is brake control, 0x30C is acc hud, 0x33D is lkas hud + int bus_fwd = -1; + + if (bus_num == 0) { + bus_fwd = 2; + } + + if (bus_num == 2) { + // block stock lkas messages and stock acc messages (if OP is doing ACC) + bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); + bool is_acc_hud_msg = addr == 0x30C; + bool is_brake_msg = addr == 0x1FA; + bool block_fwd = is_lkas_msg || is_acc_hud_msg || (is_brake_msg && !honda_fwd_brake); + if (!block_fwd) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +static int honda_bosch_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if (bus_num == 0) { + bus_fwd = 2; + } + if (bus_num == 2) { + bool is_lkas_msg = (addr == 0xE4) || (addr == 0xE5) || (addr == 0x33D) || (addr == 0x33DA) || (addr == 0x33DB); + bool is_acc_msg = ((addr == 0x1C8) || (addr == 0x30C)) && honda_bosch_radarless && honda_bosch_long; + bool block_msg = is_lkas_msg || is_acc_msg; + if (!block_msg) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +const safety_hooks honda_nidec_hooks = { + .init = honda_nidec_init, + .rx = honda_rx_hook, + .tx = honda_tx_hook, + .fwd = honda_nidec_fwd_hook, + .get_counter = honda_get_counter, + .get_checksum = honda_get_checksum, + .compute_checksum = honda_compute_checksum, +}; + +const safety_hooks honda_bosch_hooks = { + .init = honda_bosch_init, + .rx = honda_rx_hook, + .tx = honda_tx_hook, + .fwd = honda_bosch_fwd_hook, + .get_counter = honda_get_counter, + .get_checksum = honda_get_checksum, + .compute_checksum = honda_compute_checksum, +}; diff --git a/panda/board/safety/safety_hyundai.h b/panda/board/safety/safety_hyundai.h new file mode 100644 index 0000000..a849a87 --- /dev/null +++ b/panda/board/safety/safety_hyundai.h @@ -0,0 +1,356 @@ +#include "safety_hyundai_common.h" + +#define HYUNDAI_LIMITS(steer, rate_up, rate_down) { \ + .max_steer = (steer), \ + .max_rate_up = (rate_up), \ + .max_rate_down = (rate_down), \ + .max_rt_delta = 112, \ + .max_rt_interval = 250000, \ + .driver_torque_allowance = 50, \ + .driver_torque_factor = 2, \ + .type = TorqueDriverLimited, \ + /* the EPS faults when the steering angle is above a certain threshold for too long. to prevent this, */ \ + /* we allow setting CF_Lkas_ActToi bit to 0 while maintaining the requested torque value for two consecutive frames */ \ + .min_valid_request_frames = 89, \ + .max_invalid_request_frames = 2, \ + .min_valid_request_rt_interval = 810000, /* 810ms; a ~10% buffer on cutting every 90 frames */ \ + .has_steer_req_tolerance = true, \ +} + +const SteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(384, 3, 7); +const SteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3); + +const LongitudinalLimits HYUNDAI_LONG_LIMITS = { + .max_accel = 200, // 1/100 m/s2 + .min_accel = -350, // 1/100 m/s2 +}; + +const LongitudinalLimits HYUNDAI_LONG_LIMITS_SPORT = { + .max_accel = 400, // 1/100 m/s2 + .min_accel = -350, // 1/100 m/s2 +}; + +const CanMsg HYUNDAI_TX_MSGS[] = { + {0x340, 0, 8}, // LKAS11 Bus 0 + {0x4F1, 0, 4}, // CLU11 Bus 0 + {0x485, 0, 4}, // LFAHDA_MFC Bus 0 +}; + +const CanMsg HYUNDAI_LONG_TX_MSGS[] = { + {0x340, 0, 8}, // LKAS11 Bus 0 + {0x4F1, 0, 4}, // CLU11 Bus 0 + {0x485, 0, 4}, // LFAHDA_MFC Bus 0 + {0x420, 0, 8}, // SCC11 Bus 0 + {0x421, 0, 8}, // SCC12 Bus 0 + {0x50A, 0, 8}, // SCC13 Bus 0 + {0x389, 0, 8}, // SCC14 Bus 0 + {0x4A2, 0, 2}, // FRT_RADAR11 Bus 0 + {0x38D, 0, 8}, // FCA11 Bus 0 + {0x483, 0, 8}, // FCA12 Bus 0 + {0x7D0, 0, 8}, // radar UDS TX addr Bus 0 (for radar disable) +}; + +const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = { + {0x340, 0, 8}, // LKAS11 Bus 0 + {0x4F1, 2, 4}, // CLU11 Bus 2 + {0x485, 0, 4}, // LFAHDA_MFC Bus 0 +}; + +#define HYUNDAI_COMMON_RX_CHECKS(legacy) \ + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, \ + {0x371, 0, 8, .frequency = 100U}, { 0 }}}, \ + {.msg = {{0x386, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ + {.msg = {{0x394, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 7U, .frequency = 100U}, { 0 }, { 0 }}}, \ + +#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus) \ + {.msg = {{0x421, (scc_bus), 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + +RxCheck hyundai_rx_checks[] = { + HYUNDAI_COMMON_RX_CHECKS(false) + HYUNDAI_SCC12_ADDR_CHECK(0) +}; + +RxCheck hyundai_cam_scc_rx_checks[] = { + HYUNDAI_COMMON_RX_CHECKS(false) + HYUNDAI_SCC12_ADDR_CHECK(2) +}; + +RxCheck hyundai_long_rx_checks[] = { + HYUNDAI_COMMON_RX_CHECKS(false) + // Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state + {.msg = {{0x4F1, 0, 4, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +// older hyundai models have less checks due to missing counters and checksums +RxCheck hyundai_legacy_rx_checks[] = { + HYUNDAI_COMMON_RX_CHECKS(true) + HYUNDAI_SCC12_ADDR_CHECK(0) +}; + +bool hyundai_legacy = false; + + +static uint8_t hyundai_get_counter(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t cnt = 0; + if (addr == 0x260) { + cnt = (GET_BYTE(to_push, 7) >> 4) & 0x3U; + } else if (addr == 0x386) { + cnt = ((GET_BYTE(to_push, 3) >> 6) << 2) | (GET_BYTE(to_push, 1) >> 6); + } else if (addr == 0x394) { + cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7U; + } else if (addr == 0x421) { + cnt = GET_BYTE(to_push, 7) & 0xFU; + } else if (addr == 0x4F1) { + cnt = (GET_BYTE(to_push, 3) >> 4) & 0xFU; + } else { + } + return cnt; +} + +static uint32_t hyundai_get_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t chksum = 0; + if (addr == 0x260) { + chksum = GET_BYTE(to_push, 7) & 0xFU; + } else if (addr == 0x386) { + chksum = ((GET_BYTE(to_push, 7) >> 6) << 2) | (GET_BYTE(to_push, 5) >> 6); + } else if (addr == 0x394) { + chksum = GET_BYTE(to_push, 6) & 0xFU; + } else if (addr == 0x421) { + chksum = GET_BYTE(to_push, 7) >> 4; + } else { + } + return chksum; +} + +static uint32_t hyundai_compute_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t chksum = 0; + if (addr == 0x386) { + // count the bits + for (int i = 0; i < 8; i++) { + uint8_t b = GET_BYTE(to_push, i); + for (int j = 0; j < 8; j++) { + uint8_t bit = 0; + // exclude checksum and counter + if (((i != 1) || (j < 6)) && ((i != 3) || (j < 6)) && ((i != 5) || (j < 6)) && ((i != 7) || (j < 6))) { + bit = (b >> (uint8_t)j) & 1U; + } + chksum += bit; + } + } + chksum = (chksum ^ 9U) & 15U; + } else { + // sum of nibbles + for (int i = 0; i < 8; i++) { + if ((addr == 0x394) && (i == 7)) { + continue; // exclude + } + uint8_t b = GET_BYTE(to_push, i); + if (((addr == 0x260) && (i == 7)) || ((addr == 0x394) && (i == 6)) || ((addr == 0x421) && (i == 7))) { + b &= (addr == 0x421) ? 0x0FU : 0xF0U; // remove checksum + } + chksum += (b % 16U) + (b / 16U); + } + chksum = (16U - (chksum % 16U)) % 16U; + } + + return chksum; +} + +static void hyundai_rx_hook(const CANPacket_t *to_push) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + + // SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others + if ((addr == 0x421) && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) { + // 2 bits: 13-14 + int cruise_engaged = (GET_BYTES(to_push, 0, 4) >> 13) & 0x3U; + hyundai_common_cruise_state_check(cruise_engaged); + } + + if (bus == 0) { + if (addr == 0x251) { + int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU) - 1024U; + // update array of samples + update_sample(&torque_driver, torque_driver_new); + } + + // ACC steering wheel buttons + if (addr == 0x4F1) { + int cruise_button = GET_BYTE(to_push, 0) & 0x7U; + bool main_button = GET_BIT(to_push, 3U); + hyundai_common_cruise_buttons_check(cruise_button, main_button); + } + + // gas press, different for EV, hybrid, and ICE models + if ((addr == 0x371) && hyundai_ev_gas_signal) { + gas_pressed = (((GET_BYTE(to_push, 4) & 0x7FU) << 1) | GET_BYTE(to_push, 3) >> 7) != 0U; + } else if ((addr == 0x371) && hyundai_hybrid_gas_signal) { + gas_pressed = GET_BYTE(to_push, 7) != 0U; + } else if ((addr == 0x260) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { + gas_pressed = (GET_BYTE(to_push, 7) >> 6) != 0U; + } else { + } + + // sample wheel speed, averaging opposite corners + if (addr == 0x386) { + uint32_t front_left_speed = GET_BYTES(to_push, 0, 2) & 0x3FFFU; + uint32_t rear_right_speed = GET_BYTES(to_push, 6, 2) & 0x3FFFU; + vehicle_moving = (front_left_speed > HYUNDAI_STANDSTILL_THRSLD) || (rear_right_speed > HYUNDAI_STANDSTILL_THRSLD); + } + + if (addr == 0x394) { + brake_pressed = ((GET_BYTE(to_push, 5) >> 5U) & 0x3U) == 0x2U; + } + + bool stock_ecu_detected = (addr == 0x340); + + // If openpilot is controlling longitudinal we need to ensure the radar is turned off + // Enforce by checking we don't see SCC12 + if (hyundai_longitudinal && (addr == 0x421)) { + stock_ecu_detected = true; + } + generic_rx_checks(stock_ecu_detected); + } +} + +static bool hyundai_tx_hook(const CANPacket_t *to_send) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + bool tx = true; + int addr = GET_ADDR(to_send); + + // FCA11: Block any potential actuation + if (addr == 0x38D) { + int CR_VSM_DecCmd = GET_BYTE(to_send, 1); + bool FCA_CmdAct = GET_BIT(to_send, 20U); + bool CF_VSM_DecCmdAct = GET_BIT(to_send, 31U); + + if ((CR_VSM_DecCmd != 0) || FCA_CmdAct || CF_VSM_DecCmdAct) { + tx = false; + } + } + + // ACCEL: safety check + if (addr == 0x421) { + int desired_accel_raw = (((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) - 1023U; + int desired_accel_val = ((GET_BYTE(to_send, 5) << 3) | (GET_BYTE(to_send, 4) >> 5)) - 1023U; + + int aeb_decel_cmd = GET_BYTE(to_send, 2); + bool aeb_req = GET_BIT(to_send, 54U); + + bool violation = false; + + if (sport_mode) { + violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS_SPORT); + violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS_SPORT); + } else { + violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS); + violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS); + } + violation |= (aeb_decel_cmd != 0); + violation |= aeb_req; + + if (violation) { + tx = false; + } + } + + // LKA STEER: safety check + if (addr == 0x340) { + int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x7ffU) - 1024U; + bool steer_req = GET_BIT(to_send, 27U); + + const SteeringLimits limits = hyundai_alt_limits ? HYUNDAI_STEERING_LIMITS_ALT : HYUNDAI_STEERING_LIMITS; + if (steer_torque_cmd_checks(desired_torque, steer_req, limits)) { + tx = false; + } + } + + // UDS: Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address + if (addr == 0x7D0) { + if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { + tx = false; + } + } + + // BUTTONS: used for resume spamming and cruise cancellation + if ((addr == 0x4F1) && !hyundai_longitudinal) { + int button = GET_BYTE(to_send, 0) & 0x7U; + + bool allowed_resume = (button == 1) && controls_allowed; + bool allowed_cancel = (button == 4) && cruise_engaged_prev; + if (!(allowed_resume || allowed_cancel)) { + tx = false; + } + } + + return tx; +} + +static int hyundai_fwd_hook(int bus_num, int addr) { + + int bus_fwd = -1; + + // forward cam to ccan and viceversa, except lkas cmd + if (bus_num == 0) { + bus_fwd = 2; + } + if ((bus_num == 2) && (addr != 0x340) && (addr != 0x485)) { + bus_fwd = 0; + } + + return bus_fwd; +} + +static safety_config hyundai_init(uint16_t param) { + hyundai_common_init(param); + hyundai_legacy = false; + + if (hyundai_camera_scc) { + hyundai_longitudinal = false; + } + + safety_config ret; + if (hyundai_longitudinal) { + ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks, HYUNDAI_LONG_TX_MSGS); + } else if (hyundai_camera_scc) { + ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(hyundai_rx_checks, HYUNDAI_TX_MSGS); + } + return ret; +} + +static safety_config hyundai_legacy_init(uint16_t param) { + hyundai_common_init(param); + hyundai_legacy = true; + hyundai_longitudinal = false; + hyundai_camera_scc = false; + return BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS); +} + +const safety_hooks hyundai_hooks = { + .init = hyundai_init, + .rx = hyundai_rx_hook, + .tx = hyundai_tx_hook, + .fwd = hyundai_fwd_hook, + .get_counter = hyundai_get_counter, + .get_checksum = hyundai_get_checksum, + .compute_checksum = hyundai_compute_checksum, +}; + +const safety_hooks hyundai_legacy_hooks = { + .init = hyundai_legacy_init, + .rx = hyundai_rx_hook, + .tx = hyundai_tx_hook, + .fwd = hyundai_fwd_hook, + .get_counter = hyundai_get_counter, + .get_checksum = hyundai_get_checksum, + .compute_checksum = hyundai_compute_checksum, +}; diff --git a/panda/board/safety/safety_hyundai_canfd.h b/panda/board/safety/safety_hyundai_canfd.h new file mode 100644 index 0000000..fb6ccf5 --- /dev/null +++ b/panda/board/safety/safety_hyundai_canfd.h @@ -0,0 +1,360 @@ +#include "safety_hyundai_common.h" + +const SteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = { + .max_steer = 270, + .max_rt_delta = 112, + .max_rt_interval = 250000, + .max_rate_up = 2, + .max_rate_down = 3, + .driver_torque_allowance = 250, + .driver_torque_factor = 2, + .type = TorqueDriverLimited, + + // the EPS faults when the steering angle is above a certain threshold for too long. to prevent this, + // we allow setting torque actuation bit to 0 while maintaining the requested torque value for two consecutive frames + .min_valid_request_frames = 89, + .max_invalid_request_frames = 2, + .min_valid_request_rt_interval = 810000, // 810ms; a ~10% buffer on cutting every 90 frames + .has_steer_req_tolerance = true, +}; + +const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = { + {0x50, 0, 16}, // LKAS + {0x1CF, 1, 8}, // CRUISE_BUTTON + {0x2A4, 0, 24}, // CAM_0x2A4 +}; + +const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[] = { + {0x110, 0, 32}, // LKAS_ALT + {0x1CF, 1, 8}, // CRUISE_BUTTON + {0x362, 0, 32}, // CAM_0x362 +}; + +const CanMsg HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[] = { + {0x50, 0, 16}, // LKAS + {0x1CF, 1, 8}, // CRUISE_BUTTON + {0x2A4, 0, 24}, // CAM_0x2A4 + {0x51, 0, 32}, // ADRV_0x51 + {0x730, 1, 8}, // tester present for ADAS ECU disable + {0x12A, 1, 16}, // LFA + {0x160, 1, 16}, // ADRV_0x160 + {0x1E0, 1, 16}, // LFAHDA_CLUSTER + {0x1A0, 1, 32}, // CRUISE_INFO + {0x1EA, 1, 32}, // ADRV_0x1ea + {0x200, 1, 8}, // ADRV_0x200 + {0x345, 1, 8}, // ADRV_0x345 + {0x1DA, 1, 32}, // ADRV_0x1da +}; + +const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = { + {0x12A, 0, 16}, // LFA + {0x1A0, 0, 32}, // CRUISE_INFO + {0x1CF, 2, 8}, // CRUISE_BUTTON + {0x1E0, 0, 16}, // LFAHDA_CLUSTER +}; + + +// *** Addresses checked in rx hook *** +// EV, ICE, HYBRID: ACCELERATOR (0x35), ACCELERATOR_BRAKE_ALT (0x100), ACCELERATOR_ALT (0x105) +#define HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \ + {.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ + {0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ + {0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}}}, \ + {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ + {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ + +#define HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(pt_bus) \ + {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, \ + +#define HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(pt_bus) \ + {.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ + +// SCC_CONTROL (from ADAS unit or camera) +#define HYUNDAI_CANFD_SCC_ADDR_CHECK(scc_bus) \ + {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ + + +// *** Non-HDA2 checks *** +// Camera sends SCC messages on HDA1. +// Both button messages exist on some platforms, so we ensure we track the correct one using flag +RxCheck hyundai_canfd_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(2) +}; +RxCheck hyundai_canfd_alt_buttons_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(2) +}; + +// Longitudinal checks for HDA1 +RxCheck hyundai_canfd_long_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) +}; +RxCheck hyundai_canfd_long_alt_buttons_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) +}; + +// Radar sends SCC messages on these cars instead of camera +RxCheck hyundai_canfd_radar_scc_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(0) +}; +RxCheck hyundai_canfd_radar_scc_alt_buttons_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(0) + HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) + HYUNDAI_CANFD_SCC_ADDR_CHECK(0) +}; + + +// *** HDA2 checks *** +// E-CAN is on bus 1, ADAS unit sends SCC messages on HDA2. +// Does not use the alt buttons message +RxCheck hyundai_canfd_hda2_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(1) + HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) + HYUNDAI_CANFD_SCC_ADDR_CHECK(1) +}; +RxCheck hyundai_canfd_hda2_long_rx_checks[] = { + HYUNDAI_CANFD_COMMON_RX_CHECKS(1) + HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) +}; + + + +const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; +const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128; +bool hyundai_canfd_alt_buttons = false; +bool hyundai_canfd_hda2_alt_steering = false; + + +int hyundai_canfd_hda2_get_lkas_addr(void) { + return hyundai_canfd_hda2_alt_steering ? 0x110 : 0x50; +} + +static uint8_t hyundai_canfd_get_counter(const CANPacket_t *to_push) { + uint8_t ret = 0; + if (GET_LEN(to_push) == 8U) { + ret = GET_BYTE(to_push, 1) >> 4; + } else { + ret = GET_BYTE(to_push, 2); + } + return ret; +} + +static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *to_push) { + uint32_t chksum = GET_BYTE(to_push, 0) | (GET_BYTE(to_push, 1) << 8); + return chksum; +} + +static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + + const int pt_bus = hyundai_canfd_hda2 ? 1 : 0; + const int scc_bus = hyundai_camera_scc ? 2 : pt_bus; + + if (bus == pt_bus) { + // driver torque + if (addr == 0xea) { + int torque_driver_new = ((GET_BYTE(to_push, 11) & 0x1fU) << 8U) | GET_BYTE(to_push, 10); + torque_driver_new -= 4095; + update_sample(&torque_driver, torque_driver_new); + } + + // cruise buttons + const int button_addr = hyundai_canfd_alt_buttons ? 0x1aa : 0x1cf; + if (addr == button_addr) { + bool main_button = false; + int cruise_button = 0; + if (addr == 0x1cf) { + cruise_button = GET_BYTE(to_push, 2) & 0x7U; + main_button = GET_BIT(to_push, 19U); + } else { + cruise_button = (GET_BYTE(to_push, 4) >> 4) & 0x7U; + main_button = GET_BIT(to_push, 34U); + } + hyundai_common_cruise_buttons_check(cruise_button, main_button); + } + + // gas press, different for EV, hybrid, and ICE models + if ((addr == 0x35) && hyundai_ev_gas_signal) { + gas_pressed = GET_BYTE(to_push, 5) != 0U; + } else if ((addr == 0x105) && hyundai_hybrid_gas_signal) { + gas_pressed = GET_BIT(to_push, 103U) || (GET_BYTE(to_push, 13) != 0U) || GET_BIT(to_push, 112U); + } else if ((addr == 0x100) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { + gas_pressed = GET_BIT(to_push, 176U); + } else { + } + + // brake press + if (addr == 0x175) { + brake_pressed = GET_BIT(to_push, 81U); + } + + // vehicle moving + if (addr == 0xa0) { + uint32_t front_left_speed = GET_BYTES(to_push, 8, 2); + uint32_t rear_right_speed = GET_BYTES(to_push, 14, 2); + vehicle_moving = (front_left_speed > HYUNDAI_STANDSTILL_THRSLD) || (rear_right_speed > HYUNDAI_STANDSTILL_THRSLD); + } + } + + if (bus == scc_bus) { + // cruise state + if ((addr == 0x1a0) && !hyundai_longitudinal) { + // 1=enabled, 2=driver override + int cruise_status = ((GET_BYTE(to_push, 8) >> 4) & 0x7U); + bool cruise_engaged = (cruise_status == 1) || (cruise_status == 2); + hyundai_common_cruise_state_check(cruise_engaged); + } + } + + const int steer_addr = hyundai_canfd_hda2 ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a; + bool stock_ecu_detected = (addr == steer_addr) && (bus == 0); + if (hyundai_longitudinal) { + // on HDA2, ensure ADRV ECU is still knocked out + // on others, ensure accel msg is blocked from camera + const int stock_scc_bus = hyundai_canfd_hda2 ? 1 : 0; + stock_ecu_detected = stock_ecu_detected || ((addr == 0x1a0) && (bus == stock_scc_bus)); + } + generic_rx_checks(stock_ecu_detected); + +} + +static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + + // steering + const int steer_addr = (hyundai_canfd_hda2 && !hyundai_longitudinal) ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a; + if (addr == steer_addr) { + int desired_torque = (((GET_BYTE(to_send, 6) & 0xFU) << 7U) | (GET_BYTE(to_send, 5) >> 1U)) - 1024U; + bool steer_req = GET_BIT(to_send, 52U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, HYUNDAI_CANFD_STEERING_LIMITS)) { + tx = false; + } + } + + // cruise buttons check + if (addr == 0x1cf) { + int button = GET_BYTE(to_send, 2) & 0x7U; + bool is_cancel = (button == HYUNDAI_BTN_CANCEL); + bool is_resume = (button == HYUNDAI_BTN_RESUME); + + bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); + if (!allowed) { + tx = false; + } + } + + // UDS: only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address + if ((addr == 0x730) && hyundai_canfd_hda2) { + if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { + tx = false; + } + } + + // ACCEL: safety check + if (addr == 0x1a0) { + int desired_accel_raw = (((GET_BYTE(to_send, 17) & 0x7U) << 8) | GET_BYTE(to_send, 16)) - 1023U; + int desired_accel_val = ((GET_BYTE(to_send, 18) << 4) | (GET_BYTE(to_send, 17) >> 4)) - 1023U; + + bool violation = false; + + if (hyundai_longitudinal) { + violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS); + violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS); + } else { + // only used to cancel on here + if ((desired_accel_raw != 0) || (desired_accel_val != 0)) { + violation = true; + } + } + + if (violation) { + tx = false; + } + } + + return tx; +} + +static int hyundai_canfd_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if (bus_num == 0) { + bus_fwd = 2; + } + if (bus_num == 2) { + // LKAS for HDA2, LFA for HDA1 + int hda2_lfa_block_addr = hyundai_canfd_hda2_alt_steering ? 0x362 : 0x2a4; + bool is_lkas_msg = ((addr == hyundai_canfd_hda2_get_lkas_addr()) || (addr == hda2_lfa_block_addr)) && hyundai_canfd_hda2; + bool is_lfa_msg = ((addr == 0x12a) && !hyundai_canfd_hda2); + + // HUD icons + bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); + + // CRUISE_INFO for non-HDA2, we send our own longitudinal commands + bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_hda2); + + bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; + if (!block_msg) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +static safety_config hyundai_canfd_init(uint16_t param) { + hyundai_common_init(param); + + gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut); + hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS); + hyundai_canfd_hda2_alt_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING); + + // no long for radar-SCC HDA1 yet + if (!hyundai_canfd_hda2 && !hyundai_camera_scc) { + hyundai_longitudinal = false; + } + + safety_config ret; + if (hyundai_longitudinal) { + if (hyundai_canfd_hda2) { + ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS); + } else { + ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_long_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ + BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); + } + } else { + if (hyundai_canfd_hda2) { + ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS) : \ + BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_TX_MSGS); + } else if (!hyundai_camera_scc) { + ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ + BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); + } else { + ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ + BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); + } + } + + return ret; +} + +const safety_hooks hyundai_canfd_hooks = { + .init = hyundai_canfd_init, + .rx = hyundai_canfd_rx_hook, + .tx = hyundai_canfd_tx_hook, + .fwd = hyundai_canfd_fwd_hook, + .get_counter = hyundai_canfd_get_counter, + .get_checksum = hyundai_canfd_get_checksum, + .compute_checksum = hyundai_common_canfd_compute_checksum, +}; diff --git a/panda/board/safety/safety_hyundai_common.h b/panda/board/safety/safety_hyundai_common.h new file mode 100644 index 0000000..f9ea3c7 --- /dev/null +++ b/panda/board/safety/safety_hyundai_common.h @@ -0,0 +1,118 @@ +#ifndef SAFETY_HYUNDAI_COMMON_H +#define SAFETY_HYUNDAI_COMMON_H + +const int HYUNDAI_PARAM_EV_GAS = 1; +const int HYUNDAI_PARAM_HYBRID_GAS = 2; +const int HYUNDAI_PARAM_LONGITUDINAL = 4; +const int HYUNDAI_PARAM_CAMERA_SCC = 8; +const int HYUNDAI_PARAM_CANFD_HDA2 = 16; +const int HYUNDAI_PARAM_ALT_LIMITS = 64; // TODO: shift this down with the rest of the common flags + +const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms +const uint32_t HYUNDAI_STANDSTILL_THRSLD = 12; // 0.375 kph + +enum { + HYUNDAI_BTN_NONE = 0, + HYUNDAI_BTN_RESUME = 1, + HYUNDAI_BTN_SET = 2, + HYUNDAI_BTN_CANCEL = 4, +}; + +// common state +bool hyundai_ev_gas_signal = false; +bool hyundai_hybrid_gas_signal = false; +bool hyundai_longitudinal = false; +bool hyundai_camera_scc = false; +bool hyundai_canfd_hda2 = false; +bool hyundai_alt_limits = false; +uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button + +uint16_t hyundai_canfd_crc_lut[256]; + +void hyundai_common_init(uint16_t param) { + hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS); + hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS); + hyundai_camera_scc = GET_FLAG(param, HYUNDAI_PARAM_CAMERA_SCC); + hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2); + hyundai_alt_limits = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS); + + hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES; + +#ifdef ALLOW_DEBUG + hyundai_longitudinal = GET_FLAG(param, HYUNDAI_PARAM_LONGITUDINAL); +#else + hyundai_longitudinal = false; +#endif +} + +void hyundai_common_cruise_state_check(const bool cruise_engaged) { + // some newer HKG models can re-enable after spamming cancel button, + // so keep track of user button presses to deny engagement if no interaction + + // enter controls on rising edge of ACC and recent user button press, exit controls when ACC off + if (!hyundai_longitudinal) { + if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) { + controls_allowed = true; + } + + if (!cruise_engaged) { + controls_allowed = false; + } + cruise_engaged_prev = cruise_engaged; + } +} + +void hyundai_common_cruise_buttons_check(const int cruise_button, const bool main_button) { + if (main_button && main_button != cruise_main_prev) { + acc_main_on = !acc_main_on; + } + cruise_main_prev = main_button; + if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || main_button) { + hyundai_last_button_interaction = 0U; + } else { + hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES); + } + + if (hyundai_longitudinal) { + // enter controls on falling edge of resume or set + bool set = (cruise_button != HYUNDAI_BTN_SET) && (cruise_button_prev == HYUNDAI_BTN_SET); + bool res = (cruise_button != HYUNDAI_BTN_RESUME) && (cruise_button_prev == HYUNDAI_BTN_RESUME); + if (set || res) { + controls_allowed = true; + } + + // exit controls on cancel press + if (cruise_button == HYUNDAI_BTN_CANCEL) { + controls_allowed = false; + } + + cruise_button_prev = cruise_button; + } +} + +uint32_t hyundai_common_canfd_compute_checksum(const CANPacket_t *to_push) { + int len = GET_LEN(to_push); + uint32_t address = GET_ADDR(to_push); + + uint16_t crc = 0; + + for (int i = 2; i < len; i++) { + crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ GET_BYTE(to_push, i)]; + } + + // Add address to crc + crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 0U) & 0xFFU)]; + crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 8U) & 0xFFU)]; + + if (len == 24) { + crc ^= 0x819dU; + } else if (len == 32) { + crc ^= 0x9f5bU; + } else { + + } + + return crc; +} + +#endif diff --git a/panda/board/safety/safety_mazda.h b/panda/board/safety/safety_mazda.h new file mode 100644 index 0000000..b3a567f --- /dev/null +++ b/panda/board/safety/safety_mazda.h @@ -0,0 +1,130 @@ +// CAN msgs we care about +#define MAZDA_LKAS 0x243 +#define MAZDA_LKAS_HUD 0x440 +#define MAZDA_CRZ_CTRL 0x21c +#define MAZDA_CRZ_BTNS 0x09d +#define MAZDA_STEER_TORQUE 0x240 +#define MAZDA_ENGINE_DATA 0x202 +#define MAZDA_PEDALS 0x165 + +// CAN bus numbers +#define MAZDA_MAIN 0 +#define MAZDA_AUX 1 +#define MAZDA_CAM 2 + +const SteeringLimits MAZDA_STEERING_LIMITS = { + .max_steer = 800, + .max_rate_up = 10, + .max_rate_down = 25, + .max_rt_delta = 300, + .max_rt_interval = 250000, + .driver_torque_factor = 1, + .driver_torque_allowance = 15, + .type = TorqueDriverLimited, +}; + +const CanMsg MAZDA_TX_MSGS[] = {{MAZDA_LKAS, 0, 8}, {MAZDA_CRZ_BTNS, 0, 8}, {MAZDA_LKAS_HUD, 0, 8}}; + +RxCheck mazda_rx_checks[] = { + {.msg = {{MAZDA_CRZ_CTRL, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MAZDA_CRZ_BTNS, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{MAZDA_STEER_TORQUE, 0, 8, .frequency = 83U}, { 0 }, { 0 }}}, + {.msg = {{MAZDA_ENGINE_DATA, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MAZDA_PEDALS, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +// track msgs coming from OP so that we know what CAM msgs to drop and what to forward +static void mazda_rx_hook(const CANPacket_t *to_push) { + if ((int)GET_BUS(to_push) == MAZDA_MAIN) { + int addr = GET_ADDR(to_push); + + if (addr == MAZDA_ENGINE_DATA) { + // sample speed: scale by 0.01 to get kph + int speed = (GET_BYTE(to_push, 2) << 8) | GET_BYTE(to_push, 3); + vehicle_moving = speed > 10; // moving when speed > 0.1 kph + } + + if (addr == MAZDA_STEER_TORQUE) { + int torque_driver_new = GET_BYTE(to_push, 0) - 127U; + // update array of samples + update_sample(&torque_driver, torque_driver_new); + } + + // enter controls on rising edge of ACC, exit controls on ACC off + if (addr == MAZDA_CRZ_CTRL) { + acc_main_on = GET_BIT(to_push, 17U); + bool cruise_engaged = GET_BYTE(to_push, 0) & 0x8U; + pcm_cruise_check(cruise_engaged); + } + + if (addr == MAZDA_ENGINE_DATA) { + gas_pressed = (GET_BYTE(to_push, 4) || (GET_BYTE(to_push, 5) & 0xF0U)); + } + + if (addr == MAZDA_PEDALS) { + brake_pressed = (GET_BYTE(to_push, 0) & 0x10U); + } + + generic_rx_checks((addr == MAZDA_LKAS)); + } +} + +static bool mazda_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + int bus = GET_BUS(to_send); + + // Check if msg is sent on the main BUS + if (bus == MAZDA_MAIN) { + + // steer cmd checks + if (addr == MAZDA_LKAS) { + int desired_torque = (((GET_BYTE(to_send, 0) & 0x0FU) << 8) | GET_BYTE(to_send, 1)) - 2048U; + + if (steer_torque_cmd_checks(desired_torque, -1, MAZDA_STEERING_LIMITS)) { + tx = false; + } + } + + // cruise buttons check + if (addr == MAZDA_CRZ_BTNS) { + // allow resume spamming while controls allowed, but + // only allow cancel while contrls not allowed + bool cancel_cmd = (GET_BYTE(to_send, 0) == 0x1U); + if (!controls_allowed && !cancel_cmd) { + tx = false; + } + } + } + + return tx; +} + +static int mazda_fwd_hook(int bus, int addr) { + int bus_fwd = -1; + + if (bus == MAZDA_MAIN) { + bus_fwd = MAZDA_CAM; + } else if (bus == MAZDA_CAM) { + bool block = (addr == MAZDA_LKAS) || (addr == MAZDA_LKAS_HUD); + if (!block) { + bus_fwd = MAZDA_MAIN; + } + } else { + // don't fwd + } + + return bus_fwd; +} + +static safety_config mazda_init(uint16_t param) { + UNUSED(param); + return BUILD_SAFETY_CFG(mazda_rx_checks, MAZDA_TX_MSGS); +} + +const safety_hooks mazda_hooks = { + .init = mazda_init, + .rx = mazda_rx_hook, + .tx = mazda_tx_hook, + .fwd = mazda_fwd_hook, +}; diff --git a/panda/board/safety/safety_nissan.h b/panda/board/safety/safety_nissan.h new file mode 100644 index 0000000..b8db422 --- /dev/null +++ b/panda/board/safety/safety_nissan.h @@ -0,0 +1,166 @@ +const SteeringLimits NISSAN_STEERING_LIMITS = { + .angle_deg_to_can = 100, + .angle_rate_up_lookup = { + {0., 5., 15.}, + {5., .8, .15} + }, + .angle_rate_down_lookup = { + {0., 5., 15.}, + {5., 3.5, .4} + }, +}; + +const CanMsg NISSAN_TX_MSGS[] = { + {0x169, 0, 8}, // LKAS + {0x2b1, 0, 8}, // PROPILOT_HUD + {0x4cc, 0, 8}, // PROPILOT_HUD_INFO_MSG + {0x20b, 2, 6}, // CRUISE_THROTTLE (X-Trail) + {0x20b, 1, 6}, // CRUISE_THROTTLE (Altima) + {0x280, 2, 8} // CANCEL_MSG (Leaf) +}; + +// Signals duplicated below due to the fact that these messages can come in on either CAN bus, depending on car model. +RxCheck nissan_rx_checks[] = { + {.msg = {{0x1b6, 0, 8, .frequency = 100U}, + {0x1b6, 1, 8, .frequency = 100U}, { 0 }}}, // PRO_PILOT (100HZ) + {.msg = {{0x2, 0, 5, .frequency = 100U}, + {0x2, 1, 5, .frequency = 100U}, { 0 }}}, // STEER_ANGLE_SENSOR + {.msg = {{0x285, 0, 8, .frequency = 50U}, + {0x285, 1, 8, .frequency = 50U}, { 0 }}}, // WHEEL_SPEEDS_REAR + {.msg = {{0x30f, 2, 3, .frequency = 10U}, + {0x30f, 1, 3, .frequency = 10U}, { 0 }}}, // CRUISE_STATE + {.msg = {{0x15c, 0, 8, .frequency = 50U}, + {0x15c, 1, 8, .frequency = 50U}, + {0x239, 0, 8, .frequency = 50U}}}, // GAS_PEDAL + {.msg = {{0x454, 0, 8, .frequency = 10U}, + {0x454, 1, 8, .frequency = 10U}, + {0x1cc, 0, 4, .frequency = 100U}}}, // DOORS_LIGHTS / BRAKE +}; + +// EPS Location. false = V-CAN, true = C-CAN +const int NISSAN_PARAM_ALT_EPS_BUS = 1; + +bool nissan_alt_eps = false; + +static void nissan_rx_hook(const CANPacket_t *to_push) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + + if (bus == (nissan_alt_eps ? 1 : 0)) { + if (addr == 0x2) { + // Current steering angle + // Factor -0.1, little endian + int angle_meas_new = (GET_BYTES(to_push, 0, 4) & 0xFFFFU); + // Multiply by -10 to match scale of LKAS angle + angle_meas_new = to_signed(angle_meas_new, 16) * -10; + + // update array of samples + update_sample(&angle_meas, angle_meas_new); + } + + if (addr == 0x285) { + // Get current speed and standstill + uint16_t right_rear = (GET_BYTE(to_push, 0) << 8) | (GET_BYTE(to_push, 1)); + uint16_t left_rear = (GET_BYTE(to_push, 2) << 8) | (GET_BYTE(to_push, 3)); + vehicle_moving = (right_rear | left_rear) != 0U; + UPDATE_VEHICLE_SPEED((right_rear + left_rear) / 2.0 * 0.005 / 3.6); + } + + if (addr == 0x1b6) { + acc_main_on = GET_BIT(to_push, 36U); + } + + // X-Trail 0x15c, Leaf 0x239 + if ((addr == 0x15c) || (addr == 0x239)) { + if (addr == 0x15c){ + gas_pressed = ((GET_BYTE(to_push, 5) << 2) | ((GET_BYTE(to_push, 6) >> 6) & 0x3U)) > 3U; + } else { + acc_main_on = GET_BIT(to_push, 17U); + gas_pressed = GET_BYTE(to_push, 0) > 3U; + } + } + + // X-trail 0x454, Leaf 0x239 + if ((addr == 0x454) || (addr == 0x239)) { + if (addr == 0x454){ + brake_pressed = (GET_BYTE(to_push, 2) & 0x80U) != 0U; + } else { + brake_pressed = ((GET_BYTE(to_push, 4) >> 5) & 1U) != 0U; + } + } + } + + // Handle cruise enabled + if ((addr == 0x30f) && (bus == (nissan_alt_eps ? 1 : 2))) { + bool cruise_engaged = (GET_BYTE(to_push, 0) >> 3) & 1U; + pcm_cruise_check(cruise_engaged); + } + + generic_rx_checks((addr == 0x169) && (bus == 0)); +} + + +static bool nissan_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + bool violation = false; + + // steer cmd checks + if (addr == 0x169) { + int desired_angle = ((GET_BYTE(to_send, 0) << 10) | (GET_BYTE(to_send, 1) << 2) | ((GET_BYTE(to_send, 2) >> 6) & 0x3U)); + bool lka_active = (GET_BYTE(to_send, 6) >> 4) & 1U; + + // Factor is -0.01, offset is 1310. Flip to correct sign, but keep units in CAN scale + desired_angle = -desired_angle + (1310.0f * NISSAN_STEERING_LIMITS.angle_deg_to_can); + + if (steer_angle_cmd_checks(desired_angle, lka_active, NISSAN_STEERING_LIMITS)) { + violation = true; + } + } + + // acc button check, only allow cancel button to be sent + if (addr == 0x20b) { + // Violation of any button other than cancel is pressed + violation |= ((GET_BYTE(to_send, 1) & 0x3dU) > 0U); + } + + if (violation) { + tx = false; + } + + return tx; +} + + +static int nissan_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if (bus_num == 0) { + bool block_msg = (addr == 0x280); // CANCEL_MSG + if (!block_msg) { + bus_fwd = 2; // ADAS + } + } + + if (bus_num == 2) { + // 0x169 is LKAS, 0x2b1 LKAS_HUD, 0x4cc LKAS_HUD_INFO_MSG + bool block_msg = ((addr == 0x169) || (addr == 0x2b1) || (addr == 0x4cc)); + if (!block_msg) { + bus_fwd = 0; // V-CAN + } + } + + return bus_fwd; +} + +static safety_config nissan_init(uint16_t param) { + nissan_alt_eps = GET_FLAG(param, NISSAN_PARAM_ALT_EPS_BUS); + return BUILD_SAFETY_CFG(nissan_rx_checks, NISSAN_TX_MSGS); +} + +const safety_hooks nissan_hooks = { + .init = nissan_init, + .rx = nissan_rx_hook, + .tx = nissan_tx_hook, + .fwd = nissan_fwd_hook, +}; diff --git a/panda/board/safety/safety_subaru.h b/panda/board/safety/safety_subaru.h new file mode 100644 index 0000000..7c887a0 --- /dev/null +++ b/panda/board/safety/safety_subaru.h @@ -0,0 +1,295 @@ +#define SUBARU_STEERING_LIMITS_GENERATOR(steer_max, rate_up, rate_down) \ + { \ + .max_steer = (steer_max), \ + .max_rt_delta = 940, \ + .max_rt_interval = 250000, \ + .max_rate_up = (rate_up), \ + .max_rate_down = (rate_down), \ + .driver_torque_factor = 50, \ + .driver_torque_allowance = 60, \ + .type = TorqueDriverLimited, \ + /* the EPS will temporary fault if the steering rate is too high, so we cut the \ + the steering torque every 7 frames for 1 frame if the steering rate is high */ \ + .min_valid_request_frames = 7, \ + .max_invalid_request_frames = 1, \ + .min_valid_request_rt_interval = 144000, /* 10% tolerance */ \ + .has_steer_req_tolerance = true, \ + } + + +const SteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(3071, 50, 70); +const SteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); + + +const LongitudinalLimits SUBARU_LONG_LIMITS = { + .min_gas = 808, // appears to be engine braking + .max_gas = 3400, // approx 2 m/s^2 when maxing cruise_rpm and cruise_throttle + .inactive_gas = 1818, // this is zero acceleration + .max_brake = 600, // approx -3.5 m/s^2 + + .min_transmission_rpm = 0, + .max_transmission_rpm = 2400, +}; + +#define MSG_SUBARU_Brake_Status 0x13c +#define MSG_SUBARU_CruiseControl 0x240 +#define MSG_SUBARU_Throttle 0x40 +#define MSG_SUBARU_Steering_Torque 0x119 +#define MSG_SUBARU_Wheel_Speeds 0x13a + +#define MSG_SUBARU_ES_LKAS 0x122 +#define MSG_SUBARU_ES_LKAS_ANGLE 0x124 +#define MSG_SUBARU_ES_Brake 0x220 +#define MSG_SUBARU_ES_Distance 0x221 +#define MSG_SUBARU_ES_Status 0x222 +#define MSG_SUBARU_ES_DashStatus 0x321 +#define MSG_SUBARU_ES_LKAS_State 0x322 +#define MSG_SUBARU_ES_Infotainment 0x323 + +#define MSG_SUBARU_ES_UDS_Request 0x787 + +#define MSG_SUBARU_ES_HighBeamAssist 0x121 +#define MSG_SUBARU_ES_STATIC_1 0x22a +#define MSG_SUBARU_ES_STATIC_2 0x325 + +#define SUBARU_MAIN_BUS 0 +#define SUBARU_ALT_BUS 1 +#define SUBARU_CAM_BUS 2 + +#define SUBARU_COMMON_TX_MSGS(alt_bus, lkas_msg) \ + {lkas_msg, SUBARU_MAIN_BUS, 8}, \ + {MSG_SUBARU_ES_Distance, alt_bus, 8}, \ + {MSG_SUBARU_ES_DashStatus, SUBARU_MAIN_BUS, 8}, \ + {MSG_SUBARU_ES_LKAS_State, SUBARU_MAIN_BUS, 8}, \ + {MSG_SUBARU_ES_Infotainment, SUBARU_MAIN_BUS, 8}, \ + +#define SUBARU_COMMON_LONG_TX_MSGS(alt_bus) \ + {MSG_SUBARU_ES_Brake, alt_bus, 8}, \ + {MSG_SUBARU_ES_Status, alt_bus, 8}, \ + +#define SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() \ + {MSG_SUBARU_ES_UDS_Request, SUBARU_CAM_BUS, 8}, \ + {MSG_SUBARU_ES_HighBeamAssist, SUBARU_MAIN_BUS, 8}, \ + {MSG_SUBARU_ES_STATIC_1, SUBARU_MAIN_BUS, 8}, \ + {MSG_SUBARU_ES_STATIC_2, SUBARU_MAIN_BUS, 8}, \ + +#define SUBARU_COMMON_RX_CHECKS(alt_bus) \ + {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{MSG_SUBARU_CruiseControl, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 20U}, { 0 }, { 0 }}}, \ + +const CanMsg SUBARU_TX_MSGS[] = { + SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS) +}; + +const CanMsg SUBARU_LONG_TX_MSGS[] = { + SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS) + SUBARU_COMMON_LONG_TX_MSGS(SUBARU_MAIN_BUS) +}; + +const CanMsg SUBARU_GEN2_TX_MSGS[] = { + SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS) +}; + +const CanMsg SUBARU_GEN2_LONG_TX_MSGS[] = { + SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS) + SUBARU_COMMON_LONG_TX_MSGS(SUBARU_ALT_BUS) + SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() +}; + +RxCheck subaru_rx_checks[] = { + SUBARU_COMMON_RX_CHECKS(SUBARU_MAIN_BUS) +}; + +RxCheck subaru_gen2_rx_checks[] = { + SUBARU_COMMON_RX_CHECKS(SUBARU_ALT_BUS) +}; + + +const uint16_t SUBARU_PARAM_GEN2 = 1; +const uint16_t SUBARU_PARAM_LONGITUDINAL = 2; + +bool subaru_gen2 = false; +bool subaru_longitudinal = false; + + +static uint32_t subaru_get_checksum(const CANPacket_t *to_push) { + return (uint8_t)GET_BYTE(to_push, 0); +} + +static uint8_t subaru_get_counter(const CANPacket_t *to_push) { + return (uint8_t)(GET_BYTE(to_push, 1) & 0xFU); +} + +static uint32_t subaru_compute_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U); + for (int i = 1; i < len; i++) { + checksum += (uint8_t)GET_BYTE(to_push, i); + } + return checksum; +} + +static void subaru_rx_hook(const CANPacket_t *to_push) { + const int bus = GET_BUS(to_push); + const int alt_main_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; + + int addr = GET_ADDR(to_push); + if ((addr == MSG_SUBARU_Steering_Torque) && (bus == SUBARU_MAIN_BUS)) { + int torque_driver_new; + torque_driver_new = ((GET_BYTES(to_push, 0, 4) >> 16) & 0x7FFU); + torque_driver_new = -1 * to_signed(torque_driver_new, 11); + update_sample(&torque_driver, torque_driver_new); + + int angle_meas_new = (GET_BYTES(to_push, 4, 2) & 0xFFFFU); + // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units + angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); + update_sample(&angle_meas, angle_meas_new); + } + + // enter controls on rising edge of ACC, exit controls on ACC off + if ((addr == MSG_SUBARU_CruiseControl) && (bus == alt_main_bus)) { + acc_main_on = GET_BIT(to_push, 40U); + bool cruise_engaged = GET_BIT(to_push, 41U); + pcm_cruise_check(cruise_engaged); + } + + // update vehicle moving with any non-zero wheel speed + if ((addr == MSG_SUBARU_Wheel_Speeds) && (bus == alt_main_bus)) { + uint32_t fr = (GET_BYTES(to_push, 1, 3) >> 4) & 0x1FFFU; + uint32_t rr = (GET_BYTES(to_push, 3, 3) >> 1) & 0x1FFFU; + uint32_t rl = (GET_BYTES(to_push, 4, 3) >> 6) & 0x1FFFU; + uint32_t fl = (GET_BYTES(to_push, 6, 2) >> 3) & 0x1FFFU; + + vehicle_moving = (fr > 0U) || (rr > 0U) || (rl > 0U) || (fl > 0U); + + UPDATE_VEHICLE_SPEED((fr + rr + rl + fl) / 4U * 0.057); + } + + if ((addr == MSG_SUBARU_Brake_Status) && (bus == alt_main_bus)) { + brake_pressed = GET_BIT(to_push, 62U); + } + + if ((addr == MSG_SUBARU_Throttle) && (bus == SUBARU_MAIN_BUS)) { + gas_pressed = GET_BYTE(to_push, 4) != 0U; + } + + generic_rx_checks((addr == MSG_SUBARU_ES_LKAS) && (bus == SUBARU_MAIN_BUS)); +} + +static bool subaru_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + bool violation = false; + + // steer cmd checks + if (addr == MSG_SUBARU_ES_LKAS) { + int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x1FFFU); + desired_torque = -1 * to_signed(desired_torque, 13); + + bool steer_req = GET_BIT(to_send, 29U); + + const SteeringLimits limits = subaru_gen2 ? SUBARU_GEN2_STEERING_LIMITS : SUBARU_STEERING_LIMITS; + violation |= steer_torque_cmd_checks(desired_torque, steer_req, limits); + } + + // check es_brake brake_pressure limits + if (addr == MSG_SUBARU_ES_Brake) { + int es_brake_pressure = GET_BYTES(to_send, 2, 2); + violation |= longitudinal_brake_checks(es_brake_pressure, SUBARU_LONG_LIMITS); + } + + // check es_distance cruise_throttle limits + if (addr == MSG_SUBARU_ES_Distance) { + int cruise_throttle = (GET_BYTES(to_send, 2, 2) & 0x1FFFU); + bool cruise_cancel = GET_BIT(to_send, 56U); + + if (subaru_longitudinal) { + violation |= longitudinal_gas_checks(cruise_throttle, SUBARU_LONG_LIMITS); + } else { + // If openpilot is not controlling long, only allow ES_Distance for cruise cancel requests, + // (when Cruise_Cancel is true, and Cruise_Throttle is inactive) + violation |= (cruise_throttle != SUBARU_LONG_LIMITS.inactive_gas); + violation |= (!cruise_cancel); + } + } + + // check es_status transmission_rpm limits + if (addr == MSG_SUBARU_ES_Status) { + int transmission_rpm = (GET_BYTES(to_send, 2, 2) & 0x1FFFU); + violation |= longitudinal_transmission_rpm_checks(transmission_rpm, SUBARU_LONG_LIMITS); + } + + if (addr == MSG_SUBARU_ES_UDS_Request) { + // tester present ('\x02\x3E\x80\x00\x00\x00\x00\x00') is allowed for gen2 longitudinal to keep eyesight disabled + bool is_tester_present = (GET_BYTES(to_send, 0, 4) == 0x00803E02U) && (GET_BYTES(to_send, 4, 4) == 0x0U); + + // reading ES button data by identifier (b'\x03\x22\x11\x30\x00\x00\x00\x00') is also allowed (DID 0x1130) + bool is_button_rdbi = (GET_BYTES(to_send, 0, 4) == 0x30112203U) && (GET_BYTES(to_send, 4, 4) == 0x0U); + + violation |= !(is_tester_present || is_button_rdbi); + } + + if (violation){ + tx = false; + } + return tx; +} + +static int subaru_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if (bus_num == SUBARU_MAIN_BUS) { + bus_fwd = SUBARU_CAM_BUS; // to the eyesight camera + } + + if (bus_num == SUBARU_CAM_BUS) { + // Global platform + bool block_lkas = ((addr == MSG_SUBARU_ES_LKAS) || + (addr == MSG_SUBARU_ES_DashStatus) || + (addr == MSG_SUBARU_ES_LKAS_State) || + (addr == MSG_SUBARU_ES_Infotainment)); + + bool block_long = ((addr == MSG_SUBARU_ES_Brake) || + (addr == MSG_SUBARU_ES_Distance) || + (addr == MSG_SUBARU_ES_Status)); + + bool block_msg = block_lkas || (subaru_longitudinal && block_long); + if (!block_msg) { + bus_fwd = SUBARU_MAIN_BUS; // Main CAN + } + } + + return bus_fwd; +} + +static safety_config subaru_init(uint16_t param) { + subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2); + +#ifdef ALLOW_DEBUG + subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL); +#endif + + safety_config ret; + if (subaru_gen2) { + ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS); + } else { + ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_TX_MSGS); + } + return ret; +} + +const safety_hooks subaru_hooks = { + .init = subaru_init, + .rx = subaru_rx_hook, + .tx = subaru_tx_hook, + .fwd = subaru_fwd_hook, + .get_counter = subaru_get_counter, + .get_checksum = subaru_get_checksum, + .compute_checksum = subaru_compute_checksum, +}; diff --git a/panda/board/safety/safety_subaru_preglobal.h b/panda/board/safety/safety_subaru_preglobal.h new file mode 100644 index 0000000..7d44fb0 --- /dev/null +++ b/panda/board/safety/safety_subaru_preglobal.h @@ -0,0 +1,127 @@ +const SteeringLimits SUBARU_PG_STEERING_LIMITS = { + .max_steer = 2047, + .max_rt_delta = 940, + .max_rt_interval = 250000, + .max_rate_up = 50, + .max_rate_down = 70, + .driver_torque_factor = 10, + .driver_torque_allowance = 75, + .type = TorqueDriverLimited, +}; + +// Preglobal platform +// 0x161 is ES_CruiseThrottle +// 0x164 is ES_LKAS + +#define MSG_SUBARU_PG_CruiseControl 0x144 +#define MSG_SUBARU_PG_Throttle 0x140 +#define MSG_SUBARU_PG_Wheel_Speeds 0xD4 +#define MSG_SUBARU_PG_Brake_Pedal 0xD1 +#define MSG_SUBARU_PG_ES_LKAS 0x164 +#define MSG_SUBARU_PG_ES_Distance 0x161 +#define MSG_SUBARU_PG_Steering_Torque 0x371 + +#define SUBARU_PG_MAIN_BUS 0 +#define SUBARU_PG_CAM_BUS 2 + +const CanMsg SUBARU_PG_TX_MSGS[] = { + {MSG_SUBARU_PG_ES_Distance, SUBARU_PG_MAIN_BUS, 8}, + {MSG_SUBARU_PG_ES_LKAS, SUBARU_PG_MAIN_BUS, 8} +}; + +// TODO: do checksum and counter checks after adding the signals to the outback dbc file +RxCheck subaru_preglobal_rx_checks[] = { + {.msg = {{MSG_SUBARU_PG_Throttle, SUBARU_PG_MAIN_BUS, 8, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_SUBARU_PG_Steering_Torque, SUBARU_PG_MAIN_BUS, 8, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_SUBARU_PG_CruiseControl, SUBARU_PG_MAIN_BUS, 8, .frequency = 20U}, { 0 }, { 0 }}}, +}; + + +const int SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE = 1; +bool subaru_pg_reversed_driver_torque = false; + + +static void subaru_preglobal_rx_hook(const CANPacket_t *to_push) { + const int bus = GET_BUS(to_push); + + if (bus == SUBARU_PG_MAIN_BUS) { + int addr = GET_ADDR(to_push); + if (addr == MSG_SUBARU_PG_Steering_Torque) { + int torque_driver_new; + torque_driver_new = (GET_BYTE(to_push, 3) >> 5) + (GET_BYTE(to_push, 4) << 3); + torque_driver_new = to_signed(torque_driver_new, 11); + torque_driver_new = subaru_pg_reversed_driver_torque ? -torque_driver_new : torque_driver_new; + update_sample(&torque_driver, torque_driver_new); + } + + // enter controls on rising edge of ACC, exit controls on ACC off + if (addr == MSG_SUBARU_PG_CruiseControl) { + acc_main_on = GET_BIT(to_push, 48U); + bool cruise_engaged = GET_BIT(to_push, 49U); + pcm_cruise_check(cruise_engaged); + } + + // update vehicle moving with any non-zero wheel speed + if (addr == MSG_SUBARU_PG_Wheel_Speeds) { + vehicle_moving = ((GET_BYTES(to_push, 0, 4) >> 12) != 0U) || (GET_BYTES(to_push, 4, 4) != 0U); + } + + if (addr == MSG_SUBARU_PG_Brake_Pedal) { + brake_pressed = ((GET_BYTES(to_push, 0, 4) >> 16) & 0xFFU) > 0U; + } + + if (addr == MSG_SUBARU_PG_Throttle) { + gas_pressed = GET_BYTE(to_push, 0) != 0U; + } + + generic_rx_checks((addr == MSG_SUBARU_PG_ES_LKAS)); + } +} + +static bool subaru_preglobal_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + + // steer cmd checks + if (addr == MSG_SUBARU_PG_ES_LKAS) { + int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 8) & 0x1FFFU); + desired_torque = -1 * to_signed(desired_torque, 13); + + bool steer_req = GET_BIT(to_send, 24U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, SUBARU_PG_STEERING_LIMITS)) { + tx = false; + } + + } + return tx; +} + +static int subaru_preglobal_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if (bus_num == SUBARU_PG_MAIN_BUS) { + bus_fwd = SUBARU_PG_CAM_BUS; // Camera CAN + } + + if (bus_num == SUBARU_PG_CAM_BUS) { + bool block_msg = ((addr == MSG_SUBARU_PG_ES_Distance) || (addr == MSG_SUBARU_PG_ES_LKAS)); + if (!block_msg) { + bus_fwd = SUBARU_PG_MAIN_BUS; // Main CAN + } + } + + return bus_fwd; +} + +static safety_config subaru_preglobal_init(uint16_t param) { + subaru_pg_reversed_driver_torque = GET_FLAG(param, SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE); + return BUILD_SAFETY_CFG(subaru_preglobal_rx_checks, SUBARU_PG_TX_MSGS); +} + +const safety_hooks subaru_preglobal_hooks = { + .init = subaru_preglobal_init, + .rx = subaru_preglobal_rx_hook, + .tx = subaru_preglobal_tx_hook, + .fwd = subaru_preglobal_fwd_hook, +}; diff --git a/panda/board/safety/safety_tesla.h b/panda/board/safety/safety_tesla.h new file mode 100644 index 0000000..4c3a157 --- /dev/null +++ b/panda/board/safety/safety_tesla.h @@ -0,0 +1,249 @@ +const SteeringLimits TESLA_STEERING_LIMITS = { + .angle_deg_to_can = 10, + .angle_rate_up_lookup = { + {0., 5., 15.}, + {10., 1.6, .3} + }, + .angle_rate_down_lookup = { + {0., 5., 15.}, + {10., 7.0, .8} + }, +}; + +const LongitudinalLimits TESLA_LONG_LIMITS = { + .max_accel = 425, // 2. m/s^2 + .min_accel = 287, // -3.52 m/s^2 // TODO: limit to -3.48 + .inactive_accel = 375, // 0. m/s^2 +}; + + +const int TESLA_FLAG_POWERTRAIN = 1; +const int TESLA_FLAG_LONGITUDINAL_CONTROL = 2; +const int TESLA_FLAG_RAVEN = 4; + +const CanMsg TESLA_TX_MSGS[] = { + {0x488, 0, 4}, // DAS_steeringControl + {0x45, 0, 8}, // STW_ACTN_RQ + {0x45, 2, 8}, // STW_ACTN_RQ + {0x2b9, 0, 8}, // DAS_control +}; + +const CanMsg TESLA_PT_TX_MSGS[] = { + {0x2bf, 0, 8}, // DAS_control +}; + +RxCheck tesla_rx_checks[] = { + {.msg = {{0x2b9, 2, 8, .frequency = 25U}, { 0 }, { 0 }}}, // DAS_control + {.msg = {{0x370, 0, 8, .frequency = 25U}, { 0 }, { 0 }}}, // EPAS_sysStatus + {.msg = {{0x108, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque1 + {.msg = {{0x118, 0, 6, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque2 + {.msg = {{0x20a, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, // BrakeMessage + {.msg = {{0x368, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // DI_state + {.msg = {{0x318, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // GTW_carState +}; + +RxCheck tesla_raven_rx_checks[] = { + {.msg = {{0x2b9, 2, 8, .frequency = 25U}, { 0 }, { 0 }}}, // DAS_control + {.msg = {{0x131, 2, 8, .frequency = 100U}, { 0 }, { 0 }}}, // EPAS3P_sysStatus + {.msg = {{0x108, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque1 + {.msg = {{0x118, 0, 6, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque2 + {.msg = {{0x20a, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, // BrakeMessage + {.msg = {{0x368, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // DI_state + {.msg = {{0x318, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // GTW_carState +}; + +RxCheck tesla_pt_rx_checks[] = { + {.msg = {{0x106, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque1 + {.msg = {{0x116, 0, 6, .frequency = 100U}, { 0 }, { 0 }}}, // DI_torque2 + {.msg = {{0x1f8, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, // BrakeMessage + {.msg = {{0x2bf, 2, 8, .frequency = 25U}, { 0 }, { 0 }}}, // DAS_control + {.msg = {{0x256, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // DI_state +}; + +bool tesla_longitudinal = false; +bool tesla_powertrain = false; // Are we the second panda intercepting the powertrain bus? +bool tesla_raven = false; + +bool tesla_stock_aeb = false; + +static void tesla_rx_hook(const CANPacket_t *to_push) { + int bus = GET_BUS(to_push); + int addr = GET_ADDR(to_push); + + if (!tesla_powertrain) { + if((!tesla_raven && (addr == 0x370) && (bus == 0)) || (tesla_raven && (addr == 0x131) && (bus == 2))) { + // Steering angle: (0.1 * val) - 819.2 in deg. + // Store it 1/10 deg to match steering request + int angle_meas_new = (((GET_BYTE(to_push, 4) & 0x3FU) << 8) | GET_BYTE(to_push, 5)) - 8192U; + update_sample(&angle_meas, angle_meas_new); + } + } + + if(bus == 0) { + if(addr == (tesla_powertrain ? 0x116 : 0x118)) { + // Vehicle speed: ((0.05 * val) - 25) * MPH_TO_MPS + float speed = (((((GET_BYTE(to_push, 3) & 0x0FU) << 8) | (GET_BYTE(to_push, 2))) * 0.05) - 25) * 0.447; + vehicle_moving = ABS(speed) > 0.1; + UPDATE_VEHICLE_SPEED(speed); + } + + if(addr == (tesla_powertrain ? 0x106 : 0x108)) { + // Gas pressed + gas_pressed = (GET_BYTE(to_push, 6) != 0U); + } + + if(addr == (tesla_powertrain ? 0x1f8 : 0x20a)) { + // Brake pressed + brake_pressed = (((GET_BYTE(to_push, 0) & 0x0CU) >> 2) != 1U); + } + + if(addr == (tesla_powertrain ? 0x256 : 0x368)) { + // Cruise state + int cruise_state = (GET_BYTE(to_push, 1) >> 4); + + acc_main_on = (cruise_state == 1) || // STANDBY + (cruise_state == 2) || // ENABLED + (cruise_state == 3) || // STANDSTILL + (cruise_state == 4) || // OVERRIDE + (cruise_state == 6) || // PRE_FAULT + (cruise_state == 7); // PRE_CANCEL + + bool cruise_engaged = (cruise_state == 2) || // ENABLED + (cruise_state == 3) || // STANDSTILL + (cruise_state == 4) || // OVERRIDE + (cruise_state == 6) || // PRE_FAULT + (cruise_state == 7); // PRE_CANCEL + pcm_cruise_check(cruise_engaged); + } + } + + if (bus == 2) { + int das_control_addr = (tesla_powertrain ? 0x2bf : 0x2b9); + if (tesla_longitudinal && (addr == das_control_addr)) { + // "AEB_ACTIVE" + tesla_stock_aeb = ((GET_BYTE(to_push, 2) & 0x03U) == 1U); + } + } + + if (tesla_powertrain) { + // 0x2bf: DAS_control should not be received on bus 0 + generic_rx_checks((addr == 0x2bf) && (bus == 0)); + } else { + // 0x488: DAS_steeringControl should not be received on bus 0 + generic_rx_checks((addr == 0x488) && (bus == 0)); + } + +} + + +static bool tesla_tx_hook(const CANPacket_t *to_send) { + bool tx = true; + int addr = GET_ADDR(to_send); + bool violation = false; + + if(!tesla_powertrain && (addr == 0x488)) { + // Steering control: (0.1 * val) - 1638.35 in deg. + // We use 1/10 deg as a unit here + int raw_angle_can = (((GET_BYTE(to_send, 0) & 0x7FU) << 8) | GET_BYTE(to_send, 1)); + int desired_angle = raw_angle_can - 16384; + int steer_control_type = GET_BYTE(to_send, 2) >> 6; + bool steer_control_enabled = (steer_control_type != 0) && // NONE + (steer_control_type != 3); // DISABLED + + if (steer_angle_cmd_checks(desired_angle, steer_control_enabled, TESLA_STEERING_LIMITS)) { + violation = true; + } + } + + if (!tesla_powertrain && (addr == 0x45)) { + // No button other than cancel can be sent by us + int control_lever_status = (GET_BYTE(to_send, 0) & 0x3FU); + if (control_lever_status != 1) { + violation = true; + } + } + + if(addr == (tesla_powertrain ? 0x2bf : 0x2b9)) { + // DAS_control: longitudinal control message + if (tesla_longitudinal) { + // No AEB events may be sent by openpilot + int aeb_event = GET_BYTE(to_send, 2) & 0x03U; + if (aeb_event != 0) { + violation = true; + } + + // Don't send messages when the stock AEB system is active + if (tesla_stock_aeb) { + violation = true; + } + + // Don't allow any acceleration limits above the safety limits + int raw_accel_max = ((GET_BYTE(to_send, 6) & 0x1FU) << 4) | (GET_BYTE(to_send, 5) >> 4); + int raw_accel_min = ((GET_BYTE(to_send, 5) & 0x0FU) << 5) | (GET_BYTE(to_send, 4) >> 3); + violation |= longitudinal_accel_checks(raw_accel_max, TESLA_LONG_LIMITS); + violation |= longitudinal_accel_checks(raw_accel_min, TESLA_LONG_LIMITS); + } else { + violation = true; + } + } + + if (violation) { + tx = false; + } + + return tx; +} + +static int tesla_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + if(bus_num == 0) { + // Chassis/PT to autopilot + bus_fwd = 2; + } + + if(bus_num == 2) { + // Autopilot to chassis/PT + int das_control_addr = (tesla_powertrain ? 0x2bf : 0x2b9); + + bool block_msg = false; + if (!tesla_powertrain && (addr == 0x488)) { + block_msg = true; + } + + if (tesla_longitudinal && (addr == das_control_addr) && !tesla_stock_aeb) { + block_msg = true; + } + + if(!block_msg) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +static safety_config tesla_init(uint16_t param) { + tesla_powertrain = GET_FLAG(param, TESLA_FLAG_POWERTRAIN); + tesla_longitudinal = GET_FLAG(param, TESLA_FLAG_LONGITUDINAL_CONTROL); + tesla_raven = GET_FLAG(param, TESLA_FLAG_RAVEN); + + tesla_stock_aeb = false; + + safety_config ret; + if (tesla_powertrain) { + ret = BUILD_SAFETY_CFG(tesla_pt_rx_checks, TESLA_PT_TX_MSGS); + } else if (tesla_raven) { + ret = BUILD_SAFETY_CFG(tesla_raven_rx_checks, TESLA_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(tesla_rx_checks, TESLA_TX_MSGS); + } + return ret; +} + +const safety_hooks tesla_hooks = { + .init = tesla_init, + .rx = tesla_rx_hook, + .tx = tesla_tx_hook, + .fwd = tesla_fwd_hook, +}; diff --git a/panda/board/safety/safety_toyota.h b/panda/board/safety/safety_toyota.h new file mode 100644 index 0000000..538fc79 --- /dev/null +++ b/panda/board/safety/safety_toyota.h @@ -0,0 +1,434 @@ +const SteeringLimits TOYOTA_STEERING_LIMITS = { + .max_steer = 1500, + .max_rate_up = 15, // ramp up slow + .max_rate_down = 25, // ramp down fast + .max_torque_error = 350, // max torque cmd in excess of motor torque + .max_rt_delta = 450, // the real time limit is 1800/sec, a 20% buffer + .max_rt_interval = 250000, + .type = TorqueMotorLimited, + + // the EPS faults when the steering angle rate is above a certain threshold for too long. to prevent this, + // we allow setting STEER_REQUEST bit to 0 while maintaining the requested torque value for a single frame + .min_valid_request_frames = 18, + .max_invalid_request_frames = 1, + .min_valid_request_rt_interval = 170000, // 170ms; a ~10% buffer on cutting every 19 frames + .has_steer_req_tolerance = true, + + // LTA angle limits + // factor for STEER_TORQUE_SENSOR->STEER_ANGLE and STEERING_LTA->STEER_ANGLE_CMD (1 / 0.0573) + .angle_deg_to_can = 17.452007, + .angle_rate_up_lookup = { + {5., 25., 25.}, + {0.3, 0.15, 0.15} + }, + .angle_rate_down_lookup = { + {5., 25., 25.}, + {0.36, 0.26, 0.26} + }, +}; + +const int TOYOTA_LTA_MAX_ANGLE = 1657; // EPS only accepts up to 94.9461 +const int TOYOTA_LTA_MAX_MEAS_TORQUE = 1500; +const int TOYOTA_LTA_MAX_DRIVER_TORQUE = 150; + +// longitudinal limits +const LongitudinalLimits TOYOTA_LONG_LIMITS = { + .max_accel = 2000, // 2.0 m/s2 + .min_accel = -3500, // -3.5 m/s2 +}; + +const LongitudinalLimits TOYOTA_LONG_LIMITS_SPORT = { + .max_accel = 4000, // 4.0 m/s2 + .min_accel = -3500, // -3.5 m/s2 +}; + +// panda interceptor threshold needs to be equivalent to openpilot threshold to avoid controls mismatches +// If thresholds are mismatched then it is possible for panda to see the gas fall and rise while openpilot is in the pre-enabled state +// Threshold calculated from DBC gains: round((((15 + 75.555) / 0.159375) + ((15 + 151.111) / 0.159375)) / 2) = 805 +const int TOYOTA_GAS_INTERCEPTOR_THRSLD = 805; +#define TOYOTA_GET_INTERCEPTOR(msg) (((GET_BYTE((msg), 0) << 8) + GET_BYTE((msg), 1) + (GET_BYTE((msg), 2) << 8) + GET_BYTE((msg), 3)) / 2U) // avg between 2 tracks + +// Stock longitudinal +#define TOYOTA_COMMON_TX_MSGS \ + {0x2E4, 0, 5}, {0x191, 0, 8}, {0x412, 0, 8}, {0x343, 0, 8}, {0x1D2, 0, 8}, /* LKAS + LTA + ACC & PCM cancel cmds */ \ + +#define TOYOTA_COMMON_LONG_TX_MSGS \ + TOYOTA_COMMON_TX_MSGS \ + {0x283, 0, 7}, {0x2E6, 0, 8}, {0x2E7, 0, 8}, {0x33E, 0, 7}, {0x344, 0, 8}, {0x365, 0, 7}, {0x366, 0, 7}, {0x4CB, 0, 8}, /* DSU bus 0 */ \ + {0x128, 1, 6}, {0x141, 1, 4}, {0x160, 1, 8}, {0x161, 1, 7}, {0x470, 1, 4}, /* DSU bus 1 */ \ + {0x411, 0, 8}, /* PCS_HUD */ \ + {0x750, 0, 8}, /* radar diagnostic address */ \ + {0x1D3, 0, 8}, \ + +const CanMsg TOYOTA_TX_MSGS[] = { + TOYOTA_COMMON_TX_MSGS +}; + +const CanMsg TOYOTA_LONG_TX_MSGS[] = { + TOYOTA_COMMON_LONG_TX_MSGS +}; + +const CanMsg TOYOTA_INTERCEPTOR_TX_MSGS[] = { + TOYOTA_COMMON_LONG_TX_MSGS + {0x200, 0, 6}, // gas interceptor +}; + +#define TOYOTA_COMMON_RX_CHECKS(lta) \ + {.msg = {{ 0xaa, 0, 8, .check_checksum = false, .frequency = 83U}, { 0 }, { 0 }}}, \ + {.msg = {{0x260, 0, 8, .check_checksum = true, .quality_flag = (lta), .frequency = 50U}, { 0 }, { 0 }}}, \ + {.msg = {{0x1D2, 0, 8, .check_checksum = true, .frequency = 33U}, { 0 }, { 0 }}}, \ + {.msg = {{0x1D3, 0, 8, .check_checksum = true, .frequency = 33U}, { 0 }, { 0 }}}, \ + {.msg = {{0x224, 0, 8, .check_checksum = false, .frequency = 40U}, \ + {0x226, 0, 8, .check_checksum = false, .frequency = 40U}, { 0 }}}, \ + +RxCheck toyota_lka_rx_checks[] = { + TOYOTA_COMMON_RX_CHECKS(false) +}; + +RxCheck toyota_lka_interceptor_rx_checks[] = { + TOYOTA_COMMON_RX_CHECKS(false) + {.msg = {{0x201, 0, 6, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +// Check the quality flag for angle measurement when using LTA, since it's not set on TSS-P cars +RxCheck toyota_lta_rx_checks[] = { + TOYOTA_COMMON_RX_CHECKS(true) +}; + +RxCheck toyota_lta_interceptor_rx_checks[] = { + TOYOTA_COMMON_RX_CHECKS(true) + {.msg = {{0x201, 0, 6, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, +}; + +// safety param flags +// first byte is for EPS factor, second is for flags +const uint32_t TOYOTA_PARAM_OFFSET = 8U; +const uint32_t TOYOTA_EPS_FACTOR = (1UL << TOYOTA_PARAM_OFFSET) - 1U; +const uint32_t TOYOTA_PARAM_ALT_BRAKE = 1UL << TOYOTA_PARAM_OFFSET; +const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET; +const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET; +const uint32_t TOYOTA_PARAM_GAS_INTERCEPTOR = 8UL << TOYOTA_PARAM_OFFSET; + +bool toyota_alt_brake = false; +bool toyota_stock_longitudinal = false; +bool toyota_lta = false; +int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file + +static uint32_t toyota_compute_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U) + (uint8_t)(len); + for (int i = 0; i < (len - 1); i++) { + checksum += (uint8_t)GET_BYTE(to_push, i); + } + return checksum; +} + +static uint32_t toyota_get_checksum(const CANPacket_t *to_push) { + int checksum_byte = GET_LEN(to_push) - 1U; + return (uint8_t)(GET_BYTE(to_push, checksum_byte)); +} + +static uint8_t toyota_get_counter(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + uint8_t cnt = 0U; + if (addr == 0x201) { + // Signal: COUNTER_PEDAL + cnt = GET_BYTE(to_push, 4) & 0x0FU; + } + return cnt; +} + +static bool toyota_get_quality_flag_valid(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + bool valid = false; + if (addr == 0x260) { + valid = !GET_BIT(to_push, 3U); // STEER_ANGLE_INITIALIZING + } + return valid; +} + +static void toyota_rx_hook(const CANPacket_t *to_push) { + if (GET_BUS(to_push) == 0U) { + int addr = GET_ADDR(to_push); + + // get eps motor torque (0.66 factor in dbc) + if (addr == 0x260) { + int torque_meas_new = (GET_BYTE(to_push, 5) << 8) | GET_BYTE(to_push, 6); + torque_meas_new = to_signed(torque_meas_new, 16); + + // scale by dbc_factor + torque_meas_new = (torque_meas_new * toyota_dbc_eps_torque_factor) / 100; + + // update array of sample + update_sample(&torque_meas, torque_meas_new); + + // increase torque_meas by 1 to be conservative on rounding + torque_meas.min--; + torque_meas.max++; + + // driver torque for angle limiting + int torque_driver_new = (GET_BYTE(to_push, 1) << 8) | GET_BYTE(to_push, 2); + torque_driver_new = to_signed(torque_driver_new, 16); + update_sample(&torque_driver, torque_driver_new); + + // LTA request angle should match current angle while inactive, clipped to max accepted angle. + // note that angle can be relative to init angle on some TSS2 platforms, LTA has the same offset + bool steer_angle_initializing = GET_BIT(to_push, 3U); + if (!steer_angle_initializing) { + int angle_meas_new = (GET_BYTE(to_push, 3) << 8U) | GET_BYTE(to_push, 4); + angle_meas_new = CLAMP(to_signed(angle_meas_new, 16), -TOYOTA_LTA_MAX_ANGLE, TOYOTA_LTA_MAX_ANGLE); + update_sample(&angle_meas, angle_meas_new); + } + } + if (addr == 0x1D3) { + acc_main_on = GET_BIT(to_push, 15U); + } + + // enter controls on rising edge of ACC, exit controls on ACC off + // exit controls on rising edge of gas press + if (addr == 0x1D2) { + // 5th bit is CRUISE_ACTIVE + bool cruise_engaged = GET_BIT(to_push, 5U); + pcm_cruise_check(cruise_engaged); + + // sample gas pedal + if (!enable_gas_interceptor) { + gas_pressed = !GET_BIT(to_push, 4U); + } + } + + // sample speed + if (addr == 0xaa) { + int speed = 0; + // sum 4 wheel speeds. conversion: raw * 0.01 - 67.67 + for (uint8_t i = 0U; i < 8U; i += 2U) { + int wheel_speed = (GET_BYTE(to_push, i) << 8U) | GET_BYTE(to_push, (i + 1U)); + speed += wheel_speed - 6767; + } + // check that all wheel speeds are at zero value + vehicle_moving = speed != 0; + + UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 / 3.6); + } + + // most cars have brake_pressed on 0x226, corolla and rav4 on 0x224 + if (((addr == 0x224) && toyota_alt_brake) || ((addr == 0x226) && !toyota_alt_brake)) { + uint8_t bit = (addr == 0x224) ? 5U : 37U; + brake_pressed = GET_BIT(to_push, bit); + } + + // sample gas interceptor + if ((addr == 0x201) && enable_gas_interceptor) { + int gas_interceptor = TOYOTA_GET_INTERCEPTOR(to_push); + gas_pressed = gas_interceptor > TOYOTA_GAS_INTERCEPTOR_THRSLD; + + // TODO: remove this, only left in for gas_interceptor_prev test + gas_interceptor_prev = gas_interceptor; + } + + bool stock_ecu_detected = addr == 0x2E4; // STEERING_LKA + if (!toyota_stock_longitudinal && (addr == 0x343)) { + stock_ecu_detected = true; // ACC_CONTROL + } + generic_rx_checks(stock_ecu_detected); + } +} + +static bool toyota_tx_hook(const CANPacket_t *to_send) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + bool tx = true; + int addr = GET_ADDR(to_send); + int bus = GET_BUS(to_send); + + // Check if msg is sent on BUS 0 + if (bus == 0) { + + // GAS PEDAL: safety check + if (addr == 0x200) { + if (longitudinal_interceptor_checks(to_send)) { + tx = false; + } + } + + // ACCEL: safety check on byte 1-2 + if (addr == 0x343) { + int desired_accel = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); + desired_accel = to_signed(desired_accel, 16); + + bool violation = false; + if (sport_mode) { + violation |= longitudinal_accel_checks(desired_accel, TOYOTA_LONG_LIMITS_SPORT); + } else { + violation |= longitudinal_accel_checks(desired_accel, TOYOTA_LONG_LIMITS); + } + + // only ACC messages that cancel are allowed when openpilot is not controlling longitudinal + if (toyota_stock_longitudinal) { + bool cancel_req = GET_BIT(to_send, 24U); + if (!cancel_req) { + violation = true; + } + if (desired_accel != TOYOTA_LONG_LIMITS.inactive_accel) { + violation = true; + } + } + + if (violation) { + tx = false; + } + } + + // AEB: block all actuation. only used when DSU is unplugged + if (addr == 0x283) { + // only allow the checksum, which is the last byte + bool block = (GET_BYTES(to_send, 0, 4) != 0U) || (GET_BYTE(to_send, 4) != 0U) || (GET_BYTE(to_send, 5) != 0U); + if (block) { + tx = false; + } + } + + // LTA angle steering check + if (addr == 0x191) { + // check the STEER_REQUEST, STEER_REQUEST_2, TORQUE_WIND_DOWN, STEER_ANGLE_CMD signals + bool lta_request = GET_BIT(to_send, 0U); + bool lta_request2 = GET_BIT(to_send, 25U); + int torque_wind_down = GET_BYTE(to_send, 5); + int lta_angle = (GET_BYTE(to_send, 1) << 8) | GET_BYTE(to_send, 2); + lta_angle = to_signed(lta_angle, 16); + + bool steer_control_enabled = lta_request || lta_request2; + if (!toyota_lta) { + // using torque (LKA), block LTA msgs with actuation requests + if (steer_control_enabled || (lta_angle != 0) || (torque_wind_down != 0)) { + tx = false; + } + } else { + // check angle rate limits and inactive angle + if (steer_angle_cmd_checks(lta_angle, steer_control_enabled, TOYOTA_STEERING_LIMITS)) { + tx = false; + } + + if (lta_request != lta_request2) { + tx = false; + } + + // TORQUE_WIND_DOWN is gated on steer request + if (!steer_control_enabled && (torque_wind_down != 0)) { + tx = false; + } + + // TORQUE_WIND_DOWN can only be no or full torque + if ((torque_wind_down != 0) && (torque_wind_down != 100)) { + tx = false; + } + + // check if we should wind down torque + int driver_torque = MIN(ABS(torque_driver.min), ABS(torque_driver.max)); + if ((driver_torque > TOYOTA_LTA_MAX_DRIVER_TORQUE) && (torque_wind_down != 0)) { + tx = false; + } + + int eps_torque = MIN(ABS(torque_meas.min), ABS(torque_meas.max)); + if ((eps_torque > TOYOTA_LTA_MAX_MEAS_TORQUE) && (torque_wind_down != 0)) { + tx = false; + } + } + } + + // STEER: safety check on bytes 2-3 + if (addr == 0x2E4) { + int desired_torque = (GET_BYTE(to_send, 1) << 8) | GET_BYTE(to_send, 2); + desired_torque = to_signed(desired_torque, 16); + bool steer_req = GET_BIT(to_send, 0U); + // When using LTA (angle control), assert no actuation on LKA message + if (!toyota_lta) { + if (steer_torque_cmd_checks(desired_torque, steer_req, TOYOTA_STEERING_LIMITS)) { + tx = false; + } + } else { + if ((desired_torque != 0) || steer_req) { + tx = false; + } + } + } + } + + // UDS: Only tester present ("\x0F\x02\x3E\x00\x00\x00\x00\x00") allowed on diagnostics address + if (addr == 0x750) { + // this address is sub-addressed. only allow tester present to radar (0xF) + bool invalid_uds_msg = (GET_BYTES(to_send, 0, 4) != 0x003E020FU) || (GET_BYTES(to_send, 4, 4) != 0x0U); + if (invalid_uds_msg) { + tx = 0; + } + } + + return tx; +} + +static safety_config toyota_init(uint16_t param) { + toyota_alt_brake = GET_FLAG(param, TOYOTA_PARAM_ALT_BRAKE); + toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL); + toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA); + enable_gas_interceptor = GET_FLAG(param, TOYOTA_PARAM_GAS_INTERCEPTOR); + toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR; + + // Gas interceptor should not be used if openpilot is not controlling longitudinal + if (toyota_stock_longitudinal) { + enable_gas_interceptor = false; + } + + safety_config ret; + if (toyota_stock_longitudinal) { + SET_TX_MSGS(TOYOTA_TX_MSGS, ret); + } else { + enable_gas_interceptor ? SET_TX_MSGS(TOYOTA_INTERCEPTOR_TX_MSGS, ret) : \ + SET_TX_MSGS(TOYOTA_LONG_TX_MSGS, ret); + } + + if (enable_gas_interceptor) { + toyota_lta ? SET_RX_CHECKS(toyota_lta_interceptor_rx_checks, ret) : \ + SET_RX_CHECKS(toyota_lka_interceptor_rx_checks, ret); + } else { + toyota_lta ? SET_RX_CHECKS(toyota_lta_rx_checks, ret) : \ + SET_RX_CHECKS(toyota_lka_rx_checks, ret); + } + return ret; +} + +static int toyota_fwd_hook(int bus_num, int addr) { + + int bus_fwd = -1; + + if (bus_num == 0) { + bus_fwd = 2; + } + + if (bus_num == 2) { + // block stock lkas messages and stock acc messages (if OP is doing ACC) + // in TSS2, 0x191 is LTA which we need to block to avoid controls collision + bool is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191)); + // in TSS2 the camera does ACC as well, so filter 0x343 + bool is_acc_msg = (addr == 0x343); + bool block_msg = is_lkas_msg || (is_acc_msg && !toyota_stock_longitudinal); + if (!block_msg) { + bus_fwd = 0; + } + } + + return bus_fwd; +} + +const safety_hooks toyota_hooks = { + .init = toyota_init, + .rx = toyota_rx_hook, + .tx = toyota_tx_hook, + .fwd = toyota_fwd_hook, + .get_checksum = toyota_get_checksum, + .compute_checksum = toyota_compute_checksum, + .get_counter = toyota_get_counter, + .get_quality_flag_valid = toyota_get_quality_flag_valid, +}; diff --git a/panda/board/safety/safety_volkswagen_common.h b/panda/board/safety/safety_volkswagen_common.h new file mode 100644 index 0000000..ce2bf58 --- /dev/null +++ b/panda/board/safety/safety_volkswagen_common.h @@ -0,0 +1,10 @@ +#ifndef SAFETY_VOLKSWAGEN_COMMON_H +#define SAFETY_VOLKSWAGEN_COMMON_H + +const uint16_t FLAG_VOLKSWAGEN_LONG_CONTROL = 1; + +bool volkswagen_longitudinal = false; +bool volkswagen_set_button_prev = false; +bool volkswagen_resume_button_prev = false; + +#endif diff --git a/panda/board/safety/safety_volkswagen_mqb.h b/panda/board/safety/safety_volkswagen_mqb.h new file mode 100644 index 0000000..0a84342 --- /dev/null +++ b/panda/board/safety/safety_volkswagen_mqb.h @@ -0,0 +1,312 @@ +#include "safety_volkswagen_common.h" + +// lateral limits +const SteeringLimits VOLKSWAGEN_MQB_STEERING_LIMITS = { + .max_steer = 300, // 3.0 Nm (EPS side max of 3.0Nm with fault if violated) + .max_rt_delta = 75, // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75 + .max_rt_interval = 250000, // 250ms between real time checks + .max_rate_up = 4, // 2.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) + .max_rate_down = 10, // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) + .driver_torque_allowance = 80, + .driver_torque_factor = 3, + .type = TorqueDriverLimited, +}; + +// longitudinal limits +// acceleration in m/s2 * 1000 to avoid floating point math +const LongitudinalLimits VOLKSWAGEN_MQB_LONG_LIMITS = { + .max_accel = 2000, + .min_accel = -3500, + .inactive_accel = 3010, // VW sends one increment above the max range when inactive +}; + +const LongitudinalLimits VOLKSWAGEN_MQB_LONG_LIMITS_SPORT = { + .max_accel = 4000, + .min_accel = -3500, + .inactive_accel = 3010, // VW sends one increment above the max range when inactive +}; + +#define MSG_ESP_19 0x0B2 // RX from ABS, for wheel speeds +#define MSG_LH_EPS_03 0x09F // RX from EPS, for driver steering torque +#define MSG_ESP_05 0x106 // RX from ABS, for brake switch state +#define MSG_TSK_06 0x120 // RX from ECU, for ACC status from drivetrain coordinator +#define MSG_MOTOR_20 0x121 // RX from ECU, for driver throttle input +#define MSG_ACC_06 0x122 // TX by OP, ACC control instructions to the drivetrain coordinator +#define MSG_HCA_01 0x126 // TX by OP, Heading Control Assist steering torque +#define MSG_GRA_ACC_01 0x12B // TX by OP, ACC control buttons for cancel/resume +#define MSG_ACC_07 0x12E // TX by OP, ACC control instructions to the drivetrain coordinator +#define MSG_ACC_02 0x30C // TX by OP, ACC HUD data to the instrument cluster +#define MSG_MOTOR_14 0x3BE // RX from ECU, for brake switch status +#define MSG_LDW_02 0x397 // TX by OP, Lane line recognition and text alerts + +// Transmit of GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration +const CanMsg VOLKSWAGEN_MQB_STOCK_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_GRA_ACC_01, 0, 8}, {MSG_GRA_ACC_01, 2, 8}, + {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}}; +const CanMsg VOLKSWAGEN_MQB_LONG_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}, + {MSG_ACC_02, 0, 8}, {MSG_ACC_06, 0, 8}, {MSG_ACC_07, 0, 8}}; + +RxCheck volkswagen_mqb_rx_checks[] = { + {.msg = {{MSG_ESP_19, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_LH_EPS_03, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_ESP_05, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_TSK_06, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_MOTOR_20, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_MOTOR_14, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 10U}, { 0 }, { 0 }}}, + {.msg = {{MSG_GRA_ACC_01, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 33U}, { 0 }, { 0 }}}, +}; + +uint8_t volkswagen_crc8_lut_8h2f[256]; // Static lookup table for CRC8 poly 0x2F, aka 8H2F/AUTOSAR +bool volkswagen_mqb_brake_pedal_switch = false; +bool volkswagen_mqb_brake_pressure_detected = false; + +static uint32_t volkswagen_mqb_get_checksum(const CANPacket_t *to_push) { + return (uint8_t)GET_BYTE(to_push, 0); +} + +static uint8_t volkswagen_mqb_get_counter(const CANPacket_t *to_push) { + // MQB message counters are consistently found at LSB 8. + return (uint8_t)GET_BYTE(to_push, 1) & 0xFU; +} + +static uint32_t volkswagen_mqb_compute_crc(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + + // This is CRC-8H2F/AUTOSAR with a twist. See the OpenDBC implementation + // of this algorithm for a version with explanatory comments. + + uint8_t crc = 0xFFU; + for (int i = 1; i < len; i++) { + crc ^= (uint8_t)GET_BYTE(to_push, i); + crc = volkswagen_crc8_lut_8h2f[crc]; + } + + uint8_t counter = volkswagen_mqb_get_counter(to_push); + if (addr == MSG_LH_EPS_03) { + crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter]; + } else if (addr == MSG_ESP_05) { + crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter]; + } else if (addr == MSG_TSK_06) { + crc ^= (uint8_t[]){0xC4,0xE2,0x4F,0xE4,0xF8,0x2F,0x56,0x81,0x9F,0xE5,0x83,0x44,0x05,0x3F,0x97,0xDF}[counter]; + } else if (addr == MSG_MOTOR_20) { + crc ^= (uint8_t[]){0xE9,0x65,0xAE,0x6B,0x7B,0x35,0xE5,0x5F,0x4E,0xC7,0x86,0xA2,0xBB,0xDD,0xEB,0xB4}[counter]; + } else if (addr == MSG_GRA_ACC_01) { + crc ^= (uint8_t[]){0x6A,0x38,0xB4,0x27,0x22,0xEF,0xE1,0xBB,0xF8,0x80,0x84,0x49,0xC7,0x9E,0x1E,0x2B}[counter]; + } else { + // Undefined CAN message, CRC check expected to fail + } + crc = volkswagen_crc8_lut_8h2f[crc]; + + return (uint8_t)(crc ^ 0xFFU); +} + +static safety_config volkswagen_mqb_init(uint16_t param) { + UNUSED(param); + + volkswagen_set_button_prev = false; + volkswagen_resume_button_prev = false; + volkswagen_mqb_brake_pedal_switch = false; + volkswagen_mqb_brake_pressure_detected = false; + +#ifdef ALLOW_DEBUG + volkswagen_longitudinal = GET_FLAG(param, FLAG_VOLKSWAGEN_LONG_CONTROL); +#endif + gen_crc_lookup_table_8(0x2F, volkswagen_crc8_lut_8h2f); + return volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_STOCK_TX_MSGS); +} + +static void volkswagen_mqb_rx_hook(const CANPacket_t *to_push) { + if (GET_BUS(to_push) == 0U) { + int addr = GET_ADDR(to_push); + + // Update in-motion state by sampling wheel speeds + if (addr == MSG_ESP_19) { + // sum 4 wheel speeds + int speed = 0; + for (uint8_t i = 0U; i < 8U; i += 2U) { + int wheel_speed = GET_BYTE(to_push, i) | (GET_BYTE(to_push, i + 1U) << 8); + speed += wheel_speed; + } + // Check all wheel speeds for any movement + vehicle_moving = speed > 0; + } + + // Update driver input torque samples + // Signal: LH_EPS_03.EPS_Lenkmoment (absolute torque) + // Signal: LH_EPS_03.EPS_VZ_Lenkmoment (direction) + if (addr == MSG_LH_EPS_03) { + int torque_driver_new = GET_BYTE(to_push, 5) | ((GET_BYTE(to_push, 6) & 0x1FU) << 8); + int sign = (GET_BYTE(to_push, 6) & 0x80U) >> 7; + if (sign == 1) { + torque_driver_new *= -1; + } + update_sample(&torque_driver, torque_driver_new); + } + + if (addr == MSG_TSK_06) { + // When using stock ACC, enter controls on rising edge of stock ACC engage, exit on disengage + // Always exit controls on main switch off + // Signal: TSK_06.TSK_Status + int acc_status = (GET_BYTE(to_push, 3) & 0x7U); + bool cruise_engaged = (acc_status == 3) || (acc_status == 4) || (acc_status == 5); + acc_main_on = cruise_engaged || (acc_status == 2); + + if (!volkswagen_longitudinal) { + pcm_cruise_check(cruise_engaged); + } + + if (!acc_main_on) { + controls_allowed = false; + } + } + + if (addr == MSG_GRA_ACC_01) { + // If using openpilot longitudinal, enter controls on falling edge of Set or Resume with main switch on + // Signal: GRA_ACC_01.GRA_Tip_Setzen + // Signal: GRA_ACC_01.GRA_Tip_Wiederaufnahme + if (volkswagen_longitudinal) { + bool set_button = GET_BIT(to_push, 16U); + bool resume_button = GET_BIT(to_push, 19U); + if ((volkswagen_set_button_prev && !set_button) || (volkswagen_resume_button_prev && !resume_button)) { + controls_allowed = acc_main_on; + } + volkswagen_set_button_prev = set_button; + volkswagen_resume_button_prev = resume_button; + } + // Always exit controls on rising edge of Cancel + // Signal: GRA_ACC_01.GRA_Abbrechen + if (GET_BIT(to_push, 13U)) { + controls_allowed = false; + } + } + + // Signal: Motor_20.MO_Fahrpedalrohwert_01 + if (addr == MSG_MOTOR_20) { + gas_pressed = ((GET_BYTES(to_push, 0, 4) >> 12) & 0xFFU) != 0U; + } + + // Signal: Motor_14.MO_Fahrer_bremst (ECU detected brake pedal switch F63) + if (addr == MSG_MOTOR_14) { + volkswagen_mqb_brake_pedal_switch = (GET_BYTE(to_push, 3) & 0x10U) >> 4; + } + + // Signal: ESP_05.ESP_Fahrer_bremst (ESP detected driver brake pressure above platform specified threshold) + if (addr == MSG_ESP_05) { + volkswagen_mqb_brake_pressure_detected = (GET_BYTE(to_push, 3) & 0x4U) >> 2; + } + + brake_pressed = volkswagen_mqb_brake_pedal_switch || volkswagen_mqb_brake_pressure_detected; + + generic_rx_checks((addr == MSG_HCA_01)); + } +} + +static bool volkswagen_mqb_tx_hook(const CANPacket_t *to_send) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + int addr = GET_ADDR(to_send); + bool tx = true; + + // Safety check for HCA_01 Heading Control Assist torque + // Signal: HCA_01.HCA_01_LM_Offset (absolute torque) + // Signal: HCA_01.HCA_01_LM_OffSign (direction) + if (addr == MSG_HCA_01) { + int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x1U) << 8); + bool sign = GET_BIT(to_send, 31U); + if (sign) { + desired_torque *= -1; + } + + bool steer_req = GET_BIT(to_send, 30U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_MQB_STEERING_LIMITS)) { + tx = false; + } + } + + // Safety check for both ACC_06 and ACC_07 acceleration requests + // To avoid floating point math, scale upward and compare to pre-scaled safety m/s2 boundaries + if ((addr == MSG_ACC_06) || (addr == MSG_ACC_07)) { + bool violation = false; + int desired_accel = 0; + + if (addr == MSG_ACC_06) { + // Signal: ACC_06.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) + desired_accel = ((((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) * 5U) - 7220U; + } else { + // Signal: ACC_07.ACC_Folgebeschl (acceleration in m/s2, scale 0.03, offset -4.6) + int secondary_accel = (GET_BYTE(to_send, 4) * 30U) - 4600U; + violation |= (secondary_accel != 3020); // enforce always inactive (one increment above max range) at this time + // Signal: ACC_07.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) + desired_accel = (((GET_BYTE(to_send, 7) << 3) | ((GET_BYTE(to_send, 6) & 0xE0U) >> 5)) * 5U) - 7220U; + } + + if (sport_mode) { + if (desired_accel != 0) { + violation |= longitudinal_accel_checks(desired_accel, VOLKSWAGEN_MQB_LONG_LIMITS_SPORT); + } + } else { + violation |= longitudinal_accel_checks(desired_accel, VOLKSWAGEN_MQB_LONG_LIMITS); + } + + if (violation) { + tx = false; + } + } + + // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. + // This avoids unintended engagements while still allowing resume spam + if ((addr == MSG_GRA_ACC_01) && !controls_allowed) { + // disallow resume and set: bits 16 and 19 + if ((GET_BYTE(to_send, 2) & 0x9U) != 0U) { + tx = false; + } + } + + return tx; +} + +static int volkswagen_mqb_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + switch (bus_num) { + case 0: + if (addr == MSG_LH_EPS_03) { + // openpilot needs to replace apparent driver steering input torque to pacify VW Emergency Assist + bus_fwd = -1; + } else { + // Forward all remaining traffic from Extended CAN onward + bus_fwd = 2; + } + break; + case 2: + if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) { + // openpilot takes over LKAS steering control and related HUD messages from the camera + bus_fwd = -1; + } else if (volkswagen_longitudinal && ((addr == MSG_ACC_02) || (addr == MSG_ACC_06) || (addr == MSG_ACC_07))) { + // openpilot takes over acceleration/braking control and related HUD messages from the stock ACC radar + bus_fwd = -1; + } else { + // Forward all remaining traffic from Extended CAN devices to J533 gateway + bus_fwd = 0; + } + break; + default: + // No other buses should be in use; fallback to do-not-forward + bus_fwd = -1; + break; + } + + return bus_fwd; +} + +const safety_hooks volkswagen_mqb_hooks = { + .init = volkswagen_mqb_init, + .rx = volkswagen_mqb_rx_hook, + .tx = volkswagen_mqb_tx_hook, + .fwd = volkswagen_mqb_fwd_hook, + .get_counter = volkswagen_mqb_get_counter, + .get_checksum = volkswagen_mqb_get_checksum, + .compute_checksum = volkswagen_mqb_compute_crc, +}; diff --git a/panda/board/safety/safety_volkswagen_pq.h b/panda/board/safety/safety_volkswagen_pq.h new file mode 100644 index 0000000..c8838cf --- /dev/null +++ b/panda/board/safety/safety_volkswagen_pq.h @@ -0,0 +1,269 @@ +#include "safety_volkswagen_common.h" + +// lateral limits +const SteeringLimits VOLKSWAGEN_PQ_STEERING_LIMITS = { + .max_steer = 300, // 3.0 Nm (EPS side max of 3.0Nm with fault if violated) + .max_rt_delta = 113, // 6 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 75 ; 125 * 1.5 for safety pad = 113 + .max_rt_interval = 250000, // 250ms between real time checks + .max_rate_up = 6, // 3.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) + .max_rate_down = 10, // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) + .driver_torque_factor = 3, + .driver_torque_allowance = 80, + .type = TorqueDriverLimited, +}; + +// longitudinal limits +// acceleration in m/s2 * 1000 to avoid floating point math +const LongitudinalLimits VOLKSWAGEN_PQ_LONG_LIMITS = { + .max_accel = 2000, + .min_accel = -3500, + .inactive_accel = 3010, // VW sends one increment above the max range when inactive +}; + +const LongitudinalLimits VOLKSWAGEN_PQ_LONG_LIMITS_SPORT = { + .max_accel = 4000, + .min_accel = -3500, + .inactive_accel = 3010, // VW sends one increment above the max range when inactive +}; + +#define MSG_LENKHILFE_3 0x0D0 // RX from EPS, for steering angle and driver steering torque +#define MSG_HCA_1 0x0D2 // TX by OP, Heading Control Assist steering torque +#define MSG_BREMSE_1 0x1A0 // RX from ABS, for ego speed +#define MSG_MOTOR_2 0x288 // RX from ECU, for CC state and brake switch state +#define MSG_ACC_SYSTEM 0x368 // TX by OP, longitudinal acceleration controls +#define MSG_MOTOR_3 0x380 // RX from ECU, for driver throttle input +#define MSG_GRA_NEU 0x38A // TX by OP, ACC control buttons for cancel/resume +#define MSG_MOTOR_5 0x480 // RX from ECU, for ACC main switch state +#define MSG_ACC_GRA_ANZEIGE 0x56A // TX by OP, ACC HUD +#define MSG_LDW_1 0x5BE // TX by OP, Lane line recognition and text alerts + +// Transmit of GRA_Neu is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration +const CanMsg VOLKSWAGEN_PQ_STOCK_TX_MSGS[] = {{MSG_HCA_1, 0, 5}, {MSG_LDW_1, 0, 8}, + {MSG_GRA_NEU, 0, 4}, {MSG_GRA_NEU, 2, 4}}; +const CanMsg VOLKSWAGEN_PQ_LONG_TX_MSGS[] = {{MSG_HCA_1, 0, 5}, {MSG_LDW_1, 0, 8}, + {MSG_ACC_SYSTEM, 0, 8}, {MSG_ACC_GRA_ANZEIGE, 0, 8}}; + +RxCheck volkswagen_pq_rx_checks[] = { + {.msg = {{MSG_LENKHILFE_3, 0, 6, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_BREMSE_1, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_MOTOR_2, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_MOTOR_3, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{MSG_MOTOR_5, 0, 8, .check_checksum = true, .max_counter = 0U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{MSG_GRA_NEU, 0, 4, .check_checksum = true, .max_counter = 15U, .frequency = 30U}, { 0 }, { 0 }}}, +}; + +static uint32_t volkswagen_pq_get_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + + return (uint32_t)GET_BYTE(to_push, (addr == MSG_MOTOR_5) ? 7 : 0); +} + +static uint8_t volkswagen_pq_get_counter(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + uint8_t counter = 0U; + + if (addr == MSG_LENKHILFE_3) { + counter = (uint8_t)(GET_BYTE(to_push, 1) & 0xF0U) >> 4; + } else if (addr == MSG_GRA_NEU) { + counter = (uint8_t)(GET_BYTE(to_push, 2) & 0xF0U) >> 4; + } else { + } + + return counter; +} + +static uint32_t volkswagen_pq_compute_checksum(const CANPacket_t *to_push) { + int addr = GET_ADDR(to_push); + int len = GET_LEN(to_push); + uint8_t checksum = 0U; + int checksum_byte = (addr == MSG_MOTOR_5) ? 7 : 0; + + // Simple XOR over the payload, except for the byte where the checksum lives. + for (int i = 0; i < len; i++) { + if (i != checksum_byte) { + checksum ^= (uint8_t)GET_BYTE(to_push, i); + } + } + + return checksum; +} + +static safety_config volkswagen_pq_init(uint16_t param) { + UNUSED(param); + + volkswagen_set_button_prev = false; + volkswagen_resume_button_prev = false; + +#ifdef ALLOW_DEBUG + volkswagen_longitudinal = GET_FLAG(param, FLAG_VOLKSWAGEN_LONG_CONTROL); +#endif + return volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_LONG_TX_MSGS) : \ + BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_STOCK_TX_MSGS); +} + +static void volkswagen_pq_rx_hook(const CANPacket_t *to_push) { + if (GET_BUS(to_push) == 0U) { + int addr = GET_ADDR(to_push); + + // Update in-motion state from speed value. + // Signal: Bremse_1.Geschwindigkeit_neu__Bremse_1_ + if (addr == MSG_BREMSE_1) { + int speed = ((GET_BYTE(to_push, 2) & 0xFEU) >> 1) | (GET_BYTE(to_push, 3) << 7); + vehicle_moving = speed > 0; + } + + // Update driver input torque samples + // Signal: Lenkhilfe_3.LH3_LM (absolute torque) + // Signal: Lenkhilfe_3.LH3_LMSign (direction) + if (addr == MSG_LENKHILFE_3) { + int torque_driver_new = GET_BYTE(to_push, 2) | ((GET_BYTE(to_push, 3) & 0x3U) << 8); + int sign = (GET_BYTE(to_push, 3) & 0x4U) >> 2; + if (sign == 1) { + torque_driver_new *= -1; + } + update_sample(&torque_driver, torque_driver_new); + } + + if (volkswagen_longitudinal) { + if (addr == MSG_MOTOR_5) { + // ACC main switch on is a prerequisite to enter controls, exit controls immediately on main switch off + // Signal: Motor_5.GRA_Hauptschalter + acc_main_on = GET_BIT(to_push, 50U); + if (!acc_main_on) { + controls_allowed = false; + } + } + + if (addr == MSG_GRA_NEU) { + // If ACC main switch is on, enter controls on falling edge of Set or Resume + // Signal: GRA_Neu.GRA_Neu_Setzen + // Signal: GRA_Neu.GRA_Neu_Recall + bool set_button = GET_BIT(to_push, 16U); + bool resume_button = GET_BIT(to_push, 17U); + if ((volkswagen_set_button_prev && !set_button) || (volkswagen_resume_button_prev && !resume_button)) { + controls_allowed = acc_main_on; + } + volkswagen_set_button_prev = set_button; + volkswagen_resume_button_prev = resume_button; + // Exit controls on rising edge of Cancel, override Set/Resume if present simultaneously + // Signal: GRA_ACC_01.GRA_Abbrechen + if (GET_BIT(to_push, 9U)) { + controls_allowed = false; + } + } + } else { + if (addr == MSG_MOTOR_2) { + // Enter controls on rising edge of stock ACC, exit controls if stock ACC disengages + // Signal: Motor_2.GRA_Status + int acc_status = (GET_BYTE(to_push, 2) & 0xC0U) >> 6; + bool cruise_engaged = (acc_status == 1) || (acc_status == 2); + pcm_cruise_check(cruise_engaged); + } + } + + // Signal: Motor_3.Fahrpedal_Rohsignal + if (addr == MSG_MOTOR_3) { + gas_pressed = (GET_BYTE(to_push, 2)); + } + + // Signal: Motor_2.Bremslichtschalter + if (addr == MSG_MOTOR_2) { + brake_pressed = (GET_BYTE(to_push, 2) & 0x1U); + } + + generic_rx_checks((addr == MSG_HCA_1)); + } +} + +static bool volkswagen_pq_tx_hook(const CANPacket_t *to_send) { + sport_mode = alternative_experience & ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX; + + int addr = GET_ADDR(to_send); + bool tx = true; + + // Safety check for HCA_1 Heading Control Assist torque + // Signal: HCA_1.LM_Offset (absolute torque) + // Signal: HCA_1.LM_Offsign (direction) + if (addr == MSG_HCA_1) { + int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x7FU) << 8); + desired_torque = desired_torque / 32; // DBC scale from PQ network to centi-Nm + int sign = (GET_BYTE(to_send, 3) & 0x80U) >> 7; + if (sign == 1) { + desired_torque *= -1; + } + + uint32_t hca_status = ((GET_BYTE(to_send, 1) >> 4) & 0xFU); + bool steer_req = ((hca_status == 5U) || (hca_status == 7U)); + + if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_PQ_STEERING_LIMITS)) { + tx = false; + } + } + + // Safety check for acceleration commands + // To avoid floating point math, scale upward and compare to pre-scaled safety m/s2 boundaries + if (addr == MSG_ACC_SYSTEM) { + // Signal: ACC_System.ACS_Sollbeschl (acceleration in m/s2, scale 0.005, offset -7.22) + int desired_accel = ((((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) * 5U) - 7220U; + + if (sport_mode) { + if (longitudinal_accel_checks(desired_accel, VOLKSWAGEN_PQ_LONG_LIMITS_SPORT)) { + tx = false; + } + } else { + if (longitudinal_accel_checks(desired_accel, VOLKSWAGEN_PQ_LONG_LIMITS)) { + tx = false; + } + } + } + + // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. + // This avoids unintended engagements while still allowing resume spam + if ((addr == MSG_GRA_NEU) && !controls_allowed) { + // Signal: GRA_Neu.GRA_Neu_Setzen + // Signal: GRA_Neu.GRA_Neu_Recall + if (GET_BIT(to_send, 16U) || GET_BIT(to_send, 17U)) { + tx = false; + } + } + + return tx; +} + +static int volkswagen_pq_fwd_hook(int bus_num, int addr) { + int bus_fwd = -1; + + switch (bus_num) { + case 0: + // Forward all traffic from the Extended CAN onward + bus_fwd = 2; + break; + case 2: + if ((addr == MSG_HCA_1) || (addr == MSG_LDW_1)) { + // openpilot takes over LKAS steering control and related HUD messages from the camera + bus_fwd = -1; + } else if (volkswagen_longitudinal && ((addr == MSG_ACC_SYSTEM) || (addr == MSG_ACC_GRA_ANZEIGE))) { + // openpilot takes over acceleration/braking control and related HUD messages from the stock ACC radar + } else { + // Forward all remaining traffic from Extended CAN devices to J533 gateway + bus_fwd = 0; + } + break; + default: + // No other buses should be in use; fallback to do-not-forward + bus_fwd = -1; + break; + } + + return bus_fwd; +} + +const safety_hooks volkswagen_pq_hooks = { + .init = volkswagen_pq_init, + .rx = volkswagen_pq_rx_hook, + .tx = volkswagen_pq_tx_hook, + .fwd = volkswagen_pq_fwd_hook, + .get_counter = volkswagen_pq_get_counter, + .get_checksum = volkswagen_pq_get_checksum, + .compute_checksum = volkswagen_pq_compute_checksum, +}; diff --git a/panda/board/safety_declarations.h b/panda/board/safety_declarations.h new file mode 100644 index 0000000..7c5ddb9 --- /dev/null +++ b/panda/board/safety_declarations.h @@ -0,0 +1,276 @@ +#pragma once + +#define GET_BIT(msg, b) ((bool)!!(((msg)->data[((b) / 8U)] >> ((b) % 8U)) & 0x1U)) +#define GET_BYTE(msg, b) ((msg)->data[(b)]) +#define GET_FLAG(value, mask) (((__typeof__(mask))(value) & (mask)) == (mask)) + +#define BUILD_SAFETY_CFG(rx, tx) ((safety_config){(rx), (sizeof((rx)) / sizeof((rx)[0])), \ + (tx), (sizeof((tx)) / sizeof((tx)[0]))}) +#define SET_RX_CHECKS(rx, config) ((config).rx_checks = (rx), \ + (config).rx_checks_len = sizeof((rx)) / sizeof((rx)[0])) +#define SET_TX_MSGS(tx, config) ((config).tx_msgs = (tx), \ + (config).tx_msgs_len = sizeof((tx)) / sizeof((tx)[0])) +#define UPDATE_VEHICLE_SPEED(val_ms) (update_sample(&vehicle_speed, ROUND((val_ms) * VEHICLE_SPEED_FACTOR))) + +uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len) { + uint32_t ret = 0U; + for (int i = 0; i < len; i++) { + const uint32_t shift = i * 8; + ret |= (((uint32_t)msg->data[start + i]) << shift); + } + return ret; +} + +const int MAX_WRONG_COUNTERS = 5; +const uint8_t MAX_MISSED_MSGS = 10U; +#define MAX_ADDR_CHECK_MSGS 3U +#define MAX_SAMPLE_VALS 6 +// used to represent floating point vehicle speed in a sample_t +#define VEHICLE_SPEED_FACTOR 100.0 + + +// sample struct that keeps 6 samples in memory +struct sample_t { + int values[MAX_SAMPLE_VALS]; + int min; + int max; +} sample_t_default = {.values = {0}, .min = 0, .max = 0}; + +// safety code requires floats +struct lookup_t { + float x[3]; + float y[3]; +}; + +typedef struct { + int addr; + int bus; + int len; +} CanMsg; + +typedef enum { + TorqueMotorLimited, // torque steering command, limited by EPS output torque + TorqueDriverLimited, // torque steering command, limited by driver's input torque +} SteeringControlType; + +typedef struct { + // torque cmd limits + const int max_steer; + const int max_rate_up; + const int max_rate_down; + const int max_rt_delta; + const uint32_t max_rt_interval; + + const SteeringControlType type; + + // driver torque limits + const int driver_torque_allowance; + const int driver_torque_factor; + + // motor torque limits + const int max_torque_error; + + // safety around steer req bit + const int min_valid_request_frames; + const int max_invalid_request_frames; + const uint32_t min_valid_request_rt_interval; + const bool has_steer_req_tolerance; + + // angle cmd limits + const float angle_deg_to_can; + const struct lookup_t angle_rate_up_lookup; + const struct lookup_t angle_rate_down_lookup; + const int max_angle_error; // used to limit error between meas and cmd while enabled + const float angle_error_min_speed; // minimum speed to start limiting angle error + + const bool enforce_angle_error; // enables max_angle_error check + const bool inactive_angle_is_zero; // if false, enforces angle near meas when disabled (default) +} SteeringLimits; + +typedef struct { + // acceleration cmd limits + const int max_accel; + const int min_accel; + const int inactive_accel; + + // gas & brake cmd limits + // inactive and min gas are 0 on most safety modes + const int max_gas; + const int min_gas; + const int inactive_gas; + const int max_brake; + + // transmission rpm limits + const int max_transmission_rpm; + const int min_transmission_rpm; + const int inactive_transmission_rpm; + + // speed cmd limits + const int inactive_speed; +} LongitudinalLimits; + +typedef struct { + const int addr; + const int bus; + const int len; + const bool check_checksum; // true is checksum check is performed + const uint8_t max_counter; // maximum value of the counter. 0 means that the counter check is skipped + const bool quality_flag; // true is quality flag check is performed + const uint32_t frequency; // expected frequency of the message [Hz] +} CanMsgCheck; + +typedef struct { + // dynamic flags, reset on safety mode init + bool msg_seen; + int index; // if multiple messages are allowed to be checked, this stores the index of the first one seen. only msg[msg_index] will be used + bool valid_checksum; // true if and only if checksum check is passed + int wrong_counters; // counter of wrong counters, saturated between 0 and MAX_WRONG_COUNTERS + bool valid_quality_flag; // true if the message's quality/health/status signals are valid + uint8_t last_counter; // last counter value + uint32_t last_timestamp; // micro-s + bool lagging; // true if and only if the time between updates is excessive +} RxStatus; + +// params and flags about checksum, counter and frequency checks for each monitored address +typedef struct { + const CanMsgCheck msg[MAX_ADDR_CHECK_MSGS]; // check either messages (e.g. honda steer) + RxStatus status; +} RxCheck; + +typedef struct { + RxCheck *rx_checks; + int rx_checks_len; + const CanMsg *tx_msgs; + int tx_msgs_len; +} safety_config; + +typedef uint32_t (*get_checksum_t)(const CANPacket_t *to_push); +typedef uint32_t (*compute_checksum_t)(const CANPacket_t *to_push); +typedef uint8_t (*get_counter_t)(const CANPacket_t *to_push); +typedef bool (*get_quality_flag_valid_t)(const CANPacket_t *to_push); + +typedef safety_config (*safety_hook_init)(uint16_t param); +typedef void (*rx_hook)(const CANPacket_t *to_push); +typedef bool (*tx_hook)(const CANPacket_t *to_send); +typedef int (*fwd_hook)(int bus_num, int addr); + +typedef struct { + safety_hook_init init; + rx_hook rx; + tx_hook tx; + fwd_hook fwd; + get_checksum_t get_checksum; + compute_checksum_t compute_checksum; + get_counter_t get_counter; + get_quality_flag_valid_t get_quality_flag_valid; +} safety_hooks; + +bool safety_rx_hook(const CANPacket_t *to_push); +bool safety_tx_hook(CANPacket_t *to_send); +uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); +int to_signed(int d, int bits); +void update_sample(struct sample_t *sample, int sample_new); +void reset_sample(struct sample_t *sample); +bool max_limit_check(int val, const int MAX, const int MIN); +bool angle_dist_to_meas_check(int val, struct sample_t *val_meas, + const int MAX_ERROR, const int MAX_VAL); +bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, + const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR); +bool driver_limit_check(int val, int val_last, const struct sample_t *val_driver, + const int MAX, const int MAX_RATE_UP, const int MAX_RATE_DOWN, + const int MAX_ALLOWANCE, const int DRIVER_FACTOR); +bool get_longitudinal_allowed(void); +bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); +float interpolate(struct lookup_t xy, float x); +int ROUND(float val); +void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]); +void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]); +bool msg_allowed(const CANPacket_t *to_send, const CanMsg msg_list[], int len); +int get_addr_check_index(const CANPacket_t *to_push, RxCheck addr_list[], const int len); +void update_counter(RxCheck addr_list[], int index, uint8_t counter); +void update_addr_timestamp(RxCheck addr_list[], int index); +bool is_msg_valid(RxCheck addr_list[], int index); +bool rx_msg_safety_check(const CANPacket_t *to_push, + const safety_config *cfg, + const safety_hooks *safety_hooks); +void generic_rx_checks(bool stock_ecu_detected); +void relay_malfunction_set(void); +void relay_malfunction_reset(void); +bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLimits limits); +bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const SteeringLimits limits); +bool longitudinal_accel_checks(int desired_accel, const LongitudinalLimits limits); +bool longitudinal_speed_checks(int desired_speed, const LongitudinalLimits limits); +bool longitudinal_gas_checks(int desired_gas, const LongitudinalLimits limits); +bool longitudinal_transmission_rpm_checks(int desired_transmission_rpm, const LongitudinalLimits limits); +bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limits); +bool longitudinal_interceptor_checks(const CANPacket_t *to_send); +void pcm_cruise_check(bool cruise_engaged); + +void safety_tick(const safety_config *safety_config); + +// This can be set by the safety hooks +bool controls_allowed = false; +bool relay_malfunction = false; +bool enable_gas_interceptor = false; +int gas_interceptor_prev = 0; +bool gas_pressed = false; +bool gas_pressed_prev = false; +bool brake_pressed = false; +bool brake_pressed_prev = false; +bool regen_braking = false; +bool regen_braking_prev = false; +bool cruise_engaged_prev = false; +bool sport_mode = false; +struct sample_t vehicle_speed; +bool vehicle_moving = false; +bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 +int cruise_button_prev = 0; +int cruise_main_prev = 0; +bool safety_rx_checks_invalid = false; + +// for safety modes with torque steering control +int desired_torque_last = 0; // last desired steer torque +int rt_torque_last = 0; // last desired torque for real time check +int valid_steer_req_count = 0; // counter for steer request bit matching non-zero torque +int invalid_steer_req_count = 0; // counter to allow multiple frames of mismatching torque request bit +struct sample_t torque_meas; // last 6 motor torques produced by the eps +struct sample_t torque_driver; // last 6 driver torques measured +uint32_t ts_torque_check_last = 0; +uint32_t ts_steer_req_mismatch_last = 0; // last timestamp steer req was mismatched with torque + +// state for controls_allowed timeout logic +bool heartbeat_engaged = false; // openpilot enabled, passed in heartbeat USB command +uint32_t heartbeat_engaged_mismatches = 0; // count of mismatches between heartbeat_engaged and controls_allowed + +// for safety modes with angle steering control +uint32_t ts_angle_last = 0; +int desired_angle_last = 0; +struct sample_t angle_meas; // last 6 steer angles/curvatures + +// This can be set with a USB command +// It enables features that allow alternative experiences, like not disengaging on gas press +// It is only either 0 or 1 on mainline comma.ai openpilot + +#define ALT_EXP_DISABLE_DISENGAGE_ON_GAS 1 + +// If using this flag, make sure to communicate to your users that a stock safety feature is now disabled. +#define ALT_EXP_DISABLE_STOCK_AEB 2 + +// If using this flag, be aware that harder braking is more likely to lead to rear endings, +// and that alone this flag doesn't make braking compliant because there's also a time element. +// Setting this flag is used for allowing the full -5.0 to +4.0 m/s^2 at lower speeds +// See ISO 15622:2018 for more information. +#define ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX 8 + +// This flag allows AEB to be commanded from openpilot. +#define ALT_EXP_ALLOW_AEB 16 + +int alternative_experience = 0; + +// time since safety mode has been changed +uint32_t safety_mode_cnt = 0U; +// allow 1s of transition timeout after relay changes state before assessing malfunctioning +const uint32_t RELAY_TRNS_TIMEOUT = 1U; + +// Always on Lateral +#define ALT_EXP_ALWAYS_ON_LATERAL 32 diff --git a/panda/board/stm32f4/board.h b/panda/board/stm32f4/board.h new file mode 100644 index 0000000..bf95d58 --- /dev/null +++ b/panda/board/stm32f4/board.h @@ -0,0 +1,39 @@ +// ///////////////////////////////////////////////////////////// // +// Hardware abstraction layer for all different supported boards // +// ///////////////////////////////////////////////////////////// // +#include "boards/board_declarations.h" +#include "boards/unused_funcs.h" + +// ///// Board definition and detection ///// // +#include "stm32f4/lladc.h" +#include "drivers/harness.h" +#include "drivers/fan.h" +#include "stm32f4/llfan.h" +#include "drivers/clock_source.h" +#include "boards/white.h" +#include "boards/grey.h" +#include "boards/black.h" +#include "boards/uno.h" +#include "boards/dos.h" + +void detect_board_type(void) { + // SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART) + set_gpio_output(GPIOC, 14, 1); + set_gpio_output(GPIOC, 5, 1); + if(!detect_with_pull(GPIOB, 1, PULL_UP) && !detect_with_pull(GPIOB, 7, PULL_UP)){ + hw_type = HW_TYPE_DOS; + current_board = &board_dos; + } else if((detect_with_pull(GPIOA, 4, PULL_DOWN)) || (detect_with_pull(GPIOA, 5, PULL_DOWN)) || (detect_with_pull(GPIOA, 6, PULL_DOWN)) || (detect_with_pull(GPIOA, 7, PULL_DOWN))){ + hw_type = HW_TYPE_WHITE_PANDA; + current_board = &board_white; + } else if(detect_with_pull(GPIOA, 13, PULL_DOWN)) { // Rev AB deprecated, so no pullup means black. In REV C, A13 is pulled up to 5V with a 10K + hw_type = HW_TYPE_GREY_PANDA; + current_board = &board_grey; + } else if(!detect_with_pull(GPIOB, 15, PULL_UP)) { + hw_type = HW_TYPE_UNO; + current_board = &board_uno; + } else { + hw_type = HW_TYPE_BLACK_PANDA; + current_board = &board_black; + } +} diff --git a/panda/board/stm32f4/clock.h b/panda/board/stm32f4/clock.h new file mode 100644 index 0000000..19be574 --- /dev/null +++ b/panda/board/stm32f4/clock.h @@ -0,0 +1,34 @@ +void clock_init(void) { + // enable external oscillator + register_set_bits(&(RCC->CR), RCC_CR_HSEON); + while ((RCC->CR & RCC_CR_HSERDY) == 0); + + // divide things + // AHB = 96MHz + // APB1 = 48MHz + // APB2 = 48MHz + register_set(&(RCC->CFGR), RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV2, 0xFF7FFCF3U); + + // 16MHz crystal + // PLLM: 8 + // PLLN: 96 + // PLLP: 2 + // PLLQ: 4 + // P output: 96MHz + // Q output: 48MHz + register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE, 0x7F437FFFU); + + // start PLL + register_set_bits(&(RCC->CR), RCC_CR_PLLON); + while ((RCC->CR & RCC_CR_PLLRDY) == 0); + + // Configure Flash prefetch, Instruction cache, Data cache and wait state + // *** without this, it breaks *** + register_set(&(FLASH->ACR), FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS, 0x1F0FU); + + // switch to PLL + register_set_bits(&(RCC->CFGR), RCC_CFGR_SW_PLL); + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); + + // *** running on PLL *** +} diff --git a/panda/board/stm32f4/inc/cmsis_compiler.h b/panda/board/stm32f4/inc/cmsis_compiler.h new file mode 100644 index 0000000..d0f39ee --- /dev/null +++ b/panda/board/stm32f4/inc/cmsis_compiler.h @@ -0,0 +1,284 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.1.0 + * @date 09. October 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6.6 LTM (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100) + #include "cmsis_armclang_ltm.h" + + /* + * Arm Compiler above 6.10.1 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) + #include "cmsis_armclang.h" + + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #define __RESTRICT __restrict + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + + diff --git a/panda/board/stm32f4/inc/cmsis_gcc.h b/panda/board/stm32f4/inc/cmsis_gcc.h new file mode 100644 index 0000000..2f68473 --- /dev/null +++ b/panda/board/stm32f4/inc/cmsis_gcc.h @@ -0,0 +1,2169 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.2.0 + * @date 08. May 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START + +/** + \brief Initializes data and bss sections + \details This default implementations initialized all data and additional bss + sections relying on .copy.table and .zero.table specified properly + in the used linker script. + + */ +__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void) +{ + extern void _start(void) __NO_RETURN; + + typedef struct { + uint32_t const* src; + uint32_t* dest; + uint32_t wlen; + } __copy_table_t; + + typedef struct { + uint32_t* dest; + uint32_t wlen; + } __zero_table_t; + + extern const __copy_table_t __copy_table_start__; + extern const __copy_table_t __copy_table_end__; + extern const __zero_table_t __zero_table_start__; + extern const __zero_table_t __zero_table_end__; + + for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = pTable->src[i]; + } + } + + for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = 0u; + } + } + + _start(); +} + +#define __PROGRAM_START __cmsis_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __StackTop +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __StackLimit +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute((used, section(".vectors"))) +#endif + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) :: "memory"); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) :: "memory"); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ + __extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#if 0 +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) +#endif + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ + diff --git a/panda/board/stm32f4/inc/cmsis_version.h b/panda/board/stm32f4/inc/cmsis_version.h new file mode 100644 index 0000000..bf57cf3 --- /dev/null +++ b/panda/board/stm32f4/inc/cmsis_version.h @@ -0,0 +1,40 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.3 + * @date 24. June 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 3U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif + diff --git a/panda/board/stm32f4/inc/core_cm3.h b/panda/board/stm32f4/inc/core_cm3.h new file mode 100644 index 0000000..0918c5e --- /dev/null +++ b/panda/board/stm32f4/inc/core_cm3.h @@ -0,0 +1,1938 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V5.1.0 + * @date 13. March 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M3 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16U) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (3U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + This core does not support an FPU at all +*/ +#define __FPU_USED 0U + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_FP + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200U + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t _reserved1:8; /*!< bit: 16..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t _reserved1:30; /*!< bit: 2..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RESERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if defined (__CM3_REV) && (__CM3_REV < 0x0201U) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29U /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if defined (__CM3_REV) && (__CM3_REV >= 0x200U) + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1U]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#if defined (__CM3_REV) && (__CM3_REV >= 0x200U) +#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ +#endif + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[32U]; + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + __COMPILER_BARRIER(); + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __COMPILER_BARRIER(); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + (* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)) = vector; + /* ARM Application Note 321 states that the M3 does not require the architectural barrier */ +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + return (uint32_t)(* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)); +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + return 0U; /* No FPU */ +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + diff --git a/panda/board/stm32f4/inc/core_cm4.h b/panda/board/stm32f4/inc/core_cm4.h new file mode 100644 index 0000000..0d40081 --- /dev/null +++ b/panda/board/stm32f4/inc/core_cm4.h @@ -0,0 +1,2125 @@ +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V5.1.0 + * @date 13. March 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M4 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (4U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_FP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000U + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RESERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[32U]; + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x018 (R/ ) Media and FP Feature Register 2 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/* Media and FP Feature Register 2 Definitions */ + +#define FPU_MVFR2_VFP_Misc_Pos 4U /*!< MVFR2: VFP Misc bits Position */ +#define FPU_MVFR2_VFP_Misc_Msk (0xFUL << FPU_MVFR2_VFP_Misc_Pos) /*!< MVFR2: VFP Misc bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + __COMPILER_BARRIER(); + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __COMPILER_BARRIER(); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + (* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)) = vector; + /* ARM Application Note 321 states that the M4 does not require the architectural barrier */ +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + return (uint32_t)(* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)); +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + diff --git a/panda/board/stm32f4/inc/mpu_armv7.h b/panda/board/stm32f4/inc/mpu_armv7.h new file mode 100644 index 0000000..e72cc46 --- /dev/null +++ b/panda/board/stm32f4/inc/mpu_armv7.h @@ -0,0 +1,273 @@ +/****************************************************************************** + * @file mpu_armv7.h + * @brief CMSIS MPU API for Armv7-M MPU + * @version V5.1.0 + * @date 08. March 2019 + ******************************************************************************/ +/* + * Copyright (c) 2017-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV7_H +#define ARM_MPU_ARMV7_H + +#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes +#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes +#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes +#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes +#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes +#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte +#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes +#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes +#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes +#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes +#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes +#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes +#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes +#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes +#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes +#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte +#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes +#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes +#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes +#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes +#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes +#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes +#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes +#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes +#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes +#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte +#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes +#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes + +#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access +#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only +#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only +#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access +#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only +#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access + +/** MPU Region Base Address Register Value +* +* \param Region The region to be configured, number 0 to 15. +* \param BaseAddress The base address for the region. +*/ +#define ARM_MPU_RBAR(Region, BaseAddress) \ + (((BaseAddress) & MPU_RBAR_ADDR_Msk) | \ + ((Region) & MPU_RBAR_REGION_Msk) | \ + (MPU_RBAR_VALID_Msk)) + +/** +* MPU Memory Access Attributes +* +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +*/ +#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \ + ((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \ + (((IsShareable) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \ + (((IsCacheable) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \ + (((IsBufferable) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \ + ((((DisableExec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \ + (((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \ + (((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \ + (((SubRegionDisable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) | \ + (((Size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) | \ + (((MPU_RASR_ENABLE_Msk)))) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \ + ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size) + +/** +* MPU Memory Access Attribute for strongly ordered memory. +* - TEX: 000b +* - Shareable +* - Non-cacheable +* - Non-bufferable +*/ +#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U) + +/** +* MPU Memory Access Attribute for device memory. +* - TEX: 000b (if shareable) or 010b (if non-shareable) +* - Shareable or non-shareable +* - Non-cacheable +* - Bufferable (if shareable) or non-bufferable (if non-shareable) +* +* \param IsShareable Configures the device memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U)) + +/** +* MPU Memory Access Attribute for normal memory. +* - TEX: 1BBb (reflecting outer cacheability rules) +* - Shareable or non-shareable +* - Cacheable or non-cacheable (reflecting inner cacheability rules) +* - Bufferable or non-bufferable (reflecting inner cacheability rules) +* +* \param OuterCp Configures the outer cache policy. +* \param InnerCp Configures the inner cache policy. +* \param IsShareable Configures the memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) & 2U), ((InnerCp) & 1U)) + +/** +* MPU Memory Access Attribute non-cacheable policy. +*/ +#define ARM_MPU_CACHEP_NOCACHE 0U + +/** +* MPU Memory Access Attribute write-back, write and read allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_WRA 1U + +/** +* MPU Memory Access Attribute write-through, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WT_NWA 2U + +/** +* MPU Memory Access Attribute write-back, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_NWA 3U + + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; //!< The region base address register value (RBAR) + uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif + __DSB(); + __ISB(); +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DMB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + MPU->RNR = rnr; + MPU->RASR = 0U; +} + +/** Configure an MPU region. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr) +{ + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rsar Value for RSAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr) +{ + MPU->RNR = rnr; + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + while (cnt > MPU_TYPE_RALIASES) { + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize); + table += MPU_TYPE_RALIASES; + cnt -= MPU_TYPE_RALIASES; + } + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize); +} + +#endif + diff --git a/panda/board/stm32f4/inc/stm32f413xx.h b/panda/board/stm32f4/inc/stm32f413xx.h new file mode 100644 index 0000000..0962a8d --- /dev/null +++ b/panda/board/stm32f4/inc/stm32f413xx.h @@ -0,0 +1,14995 @@ +/** + ****************************************************************************** + * @file stm32f413xx.h + * @author MCD Application Team + * @version V2.6.0 + * @date 04-November-2016 + * @brief CMSIS STM32F413xx Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - peripherals registers declarations and bits definition + * - Macros to access peripheral’s registers hardware + * + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f413xx + * @{ + */ + +#ifndef __STM32F413xx_H +#define __STM32F413xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F4XX provides an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F4XX uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< FPU present */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F4XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FSMC_IRQn = 48, /*!< FSMC global Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + DFSDM1_FLT0_IRQn = 61, /*!< DFSDM1 Filter 0 global Interrupt */ + DFSDM1_FLT1_IRQn = 62, /*!< DFSDM1 Filter 1 global Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + CAN3_TX_IRQn = 74, /*!< CAN3 TX Interrupt */ + CAN3_RX0_IRQn = 75, /*!< CAN3 RX0 Interrupt */ + CAN3_RX1_IRQn = 76, /*!< CAN3 RX1 Interrupt */ + CAN3_SCE_IRQn = 77, /*!< CAN3 SCE Interrupt */ + RNG_IRQn = 80, /*!< RNG global Interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + UART9_IRQn = 88, /*!< UART9 global Interrupt */ + UART10_IRQn = 89, /*!< UART10 global Interrupt */ + QUADSPI_IRQn = 92, /*!< QuadSPI global Interrupt */ + FMPI2C1_EV_IRQn = 95, /*!< FMPI2C1 Event Interrupt */ + FMPI2C1_ER_IRQn = 96, /*!< FMPI2C1 Error Interrupt */ + LPTIM1_IRQn = 97, /*!< LP TIM1 interrupt */ + DFSDM2_FLT0_IRQn = 98, /*!< DFSDM2 Filter 0 global Interrupt */ + DFSDM2_FLT1_IRQn = 99, /*!< DFSDM2 Filter 1 global Interrupt */ + DFSDM2_FLT2_IRQn = 100, /*!< DFSDM2 Filter 2 global Interrupt */ + DFSDM2_FLT3_IRQn = 101 /*!< DFSDM2 Filter 3 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f4xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ +} CRC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1, Address offset: 0x18 */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FSMC_Bank1E_TypeDef; +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED; /*!< Reserved, 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG Configuration register2, Address offset: 0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x24-0x28 */ + __IO uint32_t CFGR; /*!< SYSCFG Configuration register, Address offset: 0x2C */ + __IO uint32_t MCHDLYCR; /*!< SYSCFG multi-channel delay register, Address offset: 0x30 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */ + __IO uint32_t DR; /*!< I2C Data register, Address offset: 0x10 */ + __IO uint32_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */ + __IO uint32_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */ + __IO uint32_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */ + __IO uint32_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */ + __IO uint32_t FLTR; /*!< I2C FLTR register, Address offset: 0x24 */ +} I2C_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< FMPI2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< FMPI2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< FMPI2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< FMPI2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< FMPI2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< FMPI2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< FMPI2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< FMPI2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< FMPI2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< FMPI2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< FMPI2C Transmit data register, Address offset: 0x28 */ +} FMPI2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ +} IWDG_TypeDef; + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PWR power control register, Address offset: 0x00 */ + __IO uint32_t CSR; /*!< PWR power control/status register, Address offset: 0x04 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + uint32_t RESERVED7; /*!< Reserved, 0x84 */ + __IO uint32_t DCKCFGR; /*!< RCC Dedicated Clocks configuration register, Address offset: 0x8C */ + __IO uint32_t CKGATENR; /*!< RCC Clocks Gated ENable Register, Address offset: 0x90 */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x94 */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CALIBR; /*!< RTC calibration register, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAFCR; /*!< RTC tamper and alternate function configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR;/*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR;/*!< RTC alarm B sub second register, Address offset: 0x48 */ + uint32_t RESERVED7; /*!< Reserved, 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 1, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDIO power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDI clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDIO argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDIO command register, Address offset: 0x0C */ + __IO const uint32_t RESPCMD; /*!< SDIO command response register, Address offset: 0x10 */ + __IO const uint32_t RESP1; /*!< SDIO response 1 register, Address offset: 0x14 */ + __IO const uint32_t RESP2; /*!< SDIO response 2 register, Address offset: 0x18 */ + __IO const uint32_t RESP3; /*!< SDIO response 3 register, Address offset: 0x1C */ + __IO const uint32_t RESP4; /*!< SDIO response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDIO data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDIO data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDIO data control register, Address offset: 0x2C */ + __IO const uint32_t DCOUNT; /*!< SDIO data counter register, Address offset: 0x30 */ + __IO const uint32_t STA; /*!< SDIO status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDIO interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDIO mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __IO const uint32_t FIFOCNT; /*!< SDIO FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDIO data FIFO register, Address offset: 0x80 */ +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; + +/** + * @brief LPTIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define FLASH_BASE 0x08000000U /*!< FLASH (up to 1.5 MB) base address in the alias region */ +#define SRAM1_BASE 0x20000000U /*!< SRAM1(256 KB) base address in the alias region */ +#define SRAM2_BASE 0x20040000U /*!< SRAM2(64 KB) base address in the alias region */ +#define PERIPH_BASE 0x40000000U /*!< Peripheral base address in the alias region */ +#define FSMC_R_BASE 0xA0000000U /*!< FSMC registers base address */ +#define QSPI_R_BASE 0xA0001000U /*!< QuadSPI registers base address */ +#define SRAM1_BB_BASE 0x22000000U /*!< SRAM1(256 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE 0x22800000U /*!< SRAM2(64 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE 0x42000000U /*!< Peripheral base address in the bit-band region */ +#define FLASH_END 0x0817FFFFU /*!< FLASH end address */ + +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000U) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000U) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000U) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000U) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400U) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800U) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00U) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000U) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400U) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800U) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00U) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000U) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400U) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800U) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00U) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000U) +#define I2S2ext_BASE (APB1PERIPH_BASE + 0x3400U) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800U) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00U) +#define I2S3ext_BASE (APB1PERIPH_BASE + 0x4000U) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400U) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800U) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00U) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000U) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400U) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800U) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00U) +#define FMPI2C1_BASE (APB1PERIPH_BASE + 0x6000U) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400U) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800U) +#define CAN3_BASE (APB1PERIPH_BASE + 0x6C00U) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000U) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400U) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800U) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00U) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000U) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400U) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000U) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400U) +#define UART9_BASE (APB2PERIPH_BASE + 0x1800U) +#define UART10_BASE (APB2PERIPH_BASE + 0x1C00U) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000U) +#define ADC_BASE (APB2PERIPH_BASE + 0x2300U) +#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00U) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000U) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400U) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800U) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00U) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000U) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400U) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800U) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000U) +#define DFSDM1_BASE (APB2PERIPH_BASE + 0x6000U) +#define DFSDM2_BASE (APB2PERIPH_BASE + 0x6400U) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00U) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20U) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40U) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60U) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100U) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180U) +#define DFSDM2_Channel0_BASE (DFSDM2_BASE + 0x00U) +#define DFSDM2_Channel1_BASE (DFSDM2_BASE + 0x20U) +#define DFSDM2_Channel2_BASE (DFSDM2_BASE + 0x40U) +#define DFSDM2_Channel3_BASE (DFSDM2_BASE + 0x60U) +#define DFSDM2_Channel4_BASE (DFSDM2_BASE + 0x80U) +#define DFSDM2_Channel5_BASE (DFSDM2_BASE + 0xA0U) +#define DFSDM2_Channel6_BASE (DFSDM2_BASE + 0xC0U) +#define DFSDM2_Channel7_BASE (DFSDM2_BASE + 0xE0U) +#define DFSDM2_Filter0_BASE (DFSDM2_BASE + 0x100U) +#define DFSDM2_Filter1_BASE (DFSDM2_BASE + 0x180U) +#define DFSDM2_Filter2_BASE (DFSDM2_BASE + 0x200U) +#define DFSDM2_Filter3_BASE (DFSDM2_BASE + 0x280U) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800U) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004U) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024U) + +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000U) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400U) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800U) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00U) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000U) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400U) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800U) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00U) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000U) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800U) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00U) +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000U) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010U) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028U) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040U) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058U) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070U) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088U) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0U) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8U) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400U) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010U) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028U) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040U) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058U) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070U) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088U) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0U) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8U) + +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800U) + + +/*!< FSMC Bankx registers base address */ +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000U) +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104U) + +/*!< Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000U +/*!< USB registers base address */ +#define USB_OTG_FS_PERIPH_BASE 0x50000000U + +#define USB_OTG_GLOBAL_BASE 0x000U +#define USB_OTG_DEVICE_BASE 0x800U +#define USB_OTG_IN_ENDPOINT_BASE 0x900U +#define USB_OTG_OUT_ENDPOINT_BASE 0xB00U +#define USB_OTG_EP_REG_SIZE 0x20U +#define USB_OTG_HOST_BASE 0x400U +#define USB_OTG_HOST_PORT_BASE 0x440U +#define USB_OTG_HOST_CHANNEL_BASE 0x500U +#define USB_OTG_HOST_CHANNEL_SIZE 0x20U +#define USB_OTG_PCGCCTL_BASE 0xE00U +#define USB_OTG_FIFO_BASE 0x1000U +#define USB_OTG_FIFO_SIZE 0x1000U + +#define UID_BASE 0x1FFF7A10U /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FFF7A22U /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FFF7BF0U /*!< Package size register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define I2S2ext ((SPI_TypeDef *) I2S2ext_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define I2S3ext ((SPI_TypeDef *) I2S3ext_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define FMPI2C1 ((FMPI2C_TypeDef *) FMPI2C1_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define CAN3 ((CAN_TypeDef *) CAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define UART10 ((USART_TypeDef *) UART10_BASE) +#define ADC ((ADC_Common_TypeDef *) ADC_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM2_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel0_BASE) +#define DFSDM2_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel1_BASE) +#define DFSDM2_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel2_BASE) +#define DFSDM2_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel3_BASE) +#define DFSDM2_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel4_BASE) +#define DFSDM2_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel5_BASE) +#define DFSDM2_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel6_BASE) +#define DFSDM2_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel7_BASE) +#define DFSDM2_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter0_BASE) +#define DFSDM2_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter1_BASE) +#define DFSDM2_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter2_BASE) +#define DFSDM2_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter3_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +/******************** Bit definition for ADC_SR register ********************/ +#define ADC_SR_AWD_Pos (0U) +#define ADC_SR_AWD_Msk (0x1U << ADC_SR_AWD_Pos) /*!< 0x00000001 */ +#define ADC_SR_AWD ADC_SR_AWD_Msk /*!
© COPYRIGHT(c) 2016 STMicroelectronics
+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx + * @{ + */ + +#ifndef __STM32F4xx_H +#define __STM32F4xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32F4) +#define STM32F4 +#endif /* STM32F4 */ + +/* Uncomment the line below according to the target STM32 device used in your + application + */ +/* #if !defined (STM32F405xx) && !defined (STM32F415xx) && !defined (STM32F407xx) && !defined (STM32F417xx) && \ + !defined (STM32F427xx) && !defined (STM32F437xx) && !defined (STM32F429xx) && !defined (STM32F439xx) && \ + !defined (STM32F401xC) && !defined (STM32F401xE) && !defined (STM32F410Tx) && !defined (STM32F410Cx) && \ + !defined (STM32F410Rx) && !defined (STM32F411xE) && !defined (STM32F446xx) && !defined (STM32F469xx) && \ + !defined (STM32F479xx) && !defined (STM32F412Cx) && !defined (STM32F412Rx) && !defined (STM32F412Vx) && \ + !defined (STM32F412Zx) && !defined (STM32F413xx) && !defined (STM32F423xx) */ + /* #define STM32F405xx */ /*!< STM32F405RG, STM32F405VG and STM32F405ZG Devices */ + /* #define STM32F415xx */ /*!< STM32F415RG, STM32F415VG and STM32F415ZG Devices */ + /* #define STM32F407xx */ /*!< STM32F407VG, STM32F407VE, STM32F407ZG, STM32F407ZE, STM32F407IG and STM32F407IE Devices */ + /* #define STM32F417xx */ /*!< STM32F417VG, STM32F417VE, STM32F417ZG, STM32F417ZE, STM32F417IG and STM32F417IE Devices */ + /* #define STM32F427xx */ /*!< STM32F427VG, STM32F427VI, STM32F427ZG, STM32F427ZI, STM32F427IG and STM32F427II Devices */ + /* #define STM32F437xx */ /*!< STM32F437VG, STM32F437VI, STM32F437ZG, STM32F437ZI, STM32F437IG and STM32F437II Devices */ + /* #define STM32F429xx */ /*!< STM32F429VG, STM32F429VI, STM32F429ZG, STM32F429ZI, STM32F429BG, STM32F429BI, STM32F429NG, + STM32F439NI, STM32F429IG and STM32F429II Devices */ + /* #define STM32F439xx */ /*!< STM32F439VG, STM32F439VI, STM32F439ZG, STM32F439ZI, STM32F439BG, STM32F439BI, STM32F439NG, + STM32F439NI, STM32F439IG and STM32F439II Devices */ + /* #define STM32F401xC */ /*!< STM32F401CB, STM32F401CC, STM32F401RB, STM32F401RC, STM32F401VB and STM32F401VC Devices */ + /* #define STM32F401xE */ /*!< STM32F401CD, STM32F401RD, STM32F401VD, STM32F401CE, STM32F401RE and STM32F401VE Devices */ + /* #define STM32F410Tx */ /*!< STM32F410T8 and STM32F410TB Devices */ + /* #define STM32F410Cx */ /*!< STM32F410C8 and STM32F410CB Devices */ + /* #define STM32F410Rx */ /*!< STM32F410R8 and STM32F410RB Devices */ + /* #define STM32F411xE */ /*!< STM32F411CC, STM32F411RC, STM32F411VC, STM32F411CE, STM32F411RE and STM32F411VE Devices */ + /* #define STM32F446xx */ /*!< STM32F446MC, STM32F446ME, STM32F446RC, STM32F446RE, STM32F446VC, STM32F446VE, STM32F446ZC, + and STM32F446ZE Devices */ + /* #define STM32F469xx */ /*!< STM32F469AI, STM32F469II, STM32F469BI, STM32F469NI, STM32F469AG, STM32F469IG, STM32F469BG, + STM32F469NG, STM32F469AE, STM32F469IE, STM32F469BE and STM32F469NE Devices */ + /* #define STM32F479xx */ /*!< STM32F479AI, STM32F479II, STM32F479BI, STM32F479NI, STM32F479AG, STM32F479IG, STM32F479BG + and STM32F479NG Devices */ + /* #define STM32F412Cx */ /*!< STM32F412CEU and STM32F412CGU Devices */ + /* #define STM32F412Zx */ /*!< STM32F412ZET, STM32F412ZGT, STM32F412ZEJ and STM32F412ZGJ Devices */ + /* #define STM32F412Vx */ /*!< STM32F412VET, STM32F412VGT, STM32F412VEH and STM32F412VGH Devices */ + /* #define STM32F412Rx */ /*!< STM32F412RET, STM32F412RGT, STM32F412REY and STM32F412RGY Devices */ + /* #define STM32F413xx */ /*!< STM32F413CH, STM32F413MH, STM32F413RH, STM32F413VH, STM32F413ZH, STM32F413CG, STM32F413MG, + STM32F413RG, STM32F413VG and STM32F413ZG Devices */ + /* #define STM32F423xx */ /*!< STM32F423CH, STM32F423RH, STM32F423VH and STM32F423ZH Devices */ +//#endif + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS version number V2.6.0 + */ +#define __STM32F4xx_CMSIS_VERSION_MAIN (0x02U) /*!< [31:24] main version */ +#define __STM32F4xx_CMSIS_VERSION_SUB1 (0x06U) /*!< [23:16] sub1 version */ +#define __STM32F4xx_CMSIS_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */ +#define __STM32F4xx_CMSIS_VERSION_RC (0x00U) /*!< [7:0] release candidate */ +#define __STM32F4xx_CMSIS_VERSION ((__STM32F4xx_CMSIS_VERSION_MAIN << 24)\ + |(__STM32F4xx_CMSIS_VERSION_SUB1 << 16)\ + |(__STM32F4xx_CMSIS_VERSION_SUB2 << 8 )\ + |(__STM32F4xx_CMSIS_VERSION)) + +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ + +// #if defined(STM32F405xx) +// #include "stm32f405xx.h" +// #elif defined(STM32F415xx) +// #include "stm32f415xx.h" +// #elif defined(STM32F407xx) +// #include "stm32f407xx.h" +// #elif defined(STM32F417xx) +// #include "stm32f417xx.h" +// #elif defined(STM32F427xx) +// #include "stm32f427xx.h" +// #elif defined(STM32F437xx) +// #include "stm32f437xx.h" +// #elif defined(STM32F429xx) +// #include "stm32f429xx.h" +// #elif defined(STM32F439xx) +// #include "stm32f439xx.h" +// #elif defined(STM32F401xC) +// #include "stm32f401xc.h" +// #elif defined(STM32F401xE) +// #include "stm32f401xe.h" +// #elif defined(STM32F410Tx) +// #include "stm32f410tx.h" +// #elif defined(STM32F410Cx) +// #include "stm32f410cx.h" +// #elif defined(STM32F410Rx) +// #include "stm32f410rx.h" +// #elif defined(STM32F411xE) +// #include "stm32f411xe.h" +// #elif defined(STM32F446xx) +// #include "stm32f446xx.h" +// #elif defined(STM32F469xx) +// #include "stm32f469xx.h" +// #elif defined(STM32F479xx) +// #include "stm32f479xx.h" +// #elif defined(STM32F412Cx) +// #include "stm32f412cx.h" +// #elif defined(STM32F412Zx) +// #include "stm32f412zx.h" +// #elif defined(STM32F412Rx) +// #include "stm32f412rx.h" +// #elif defined(STM32F412Vx) +// #include "stm32f412vx.h" +#if defined(STM32F413xx) + #include "stm32f413xx.h" + #elif defined(STM32F423xx) + #include "stm32f423xx.h" +#else + #error "Please select first the target STM32F4xx device used in your application (in stm32f4xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0U, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0U, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + ERROR = 0U, + SUCCESS = !ERROR +} ErrorStatus; + +/** + * @} + */ + + +/** @addtogroup Exported_macro + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + + +/** + * @} + */ + +#if defined (USE_HAL_DRIVER) + #include "stm32f4xx_hal.h" +#endif /* USE_HAL_DRIVER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __STM32F4xx_H */ +/** + * @} + */ + +/** + * @} + */ + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32f4/inc/stm32f4xx_hal_def.h b/panda/board/stm32f4/inc/stm32f4xx_hal_def.h new file mode 100644 index 0000000..b77f179 --- /dev/null +++ b/panda/board/stm32f4/inc/stm32f4xx_hal_def.h @@ -0,0 +1,214 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_def.h + * @author MCD Application Team + * @version V1.6.0 + * @date 04-November-2016 + * @brief This file contains HAL common defines, enumeration, macros and + * structures definitions. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_DEF +#define __STM32F4xx_HAL_DEF + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx.h" +//#include "Legacy/stm32_hal_legacy.h" +//#include + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief HAL Status structures definition + */ +typedef enum +{ + HAL_OK = 0x00U, + HAL_ERROR = 0x01U, + HAL_BUSY = 0x02U, + HAL_TIMEOUT = 0x03U +} HAL_StatusTypeDef; + +/** + * @brief HAL Lock structures definition + */ +typedef enum +{ + HAL_UNLOCKED = 0x00U, + HAL_LOCKED = 0x01U +} HAL_LockTypeDef; + +/* Exported macro ------------------------------------------------------------*/ +#define HAL_MAX_DELAY 0xFFFFFFFFU + +#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != RESET) +#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == RESET) + +#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \ + do{ \ + (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \ + (__DMA_HANDLE__).Parent = (__HANDLE__); \ + } while(0) + +#define UNUSED(x) ((void)(x)) + +/** @brief Reset the Handle's State field. + * @param __HANDLE__: specifies the Peripheral Handle. + * @note This macro can be used for the following purpose: + * - When the Handle is declared as local variable; before passing it as parameter + * to HAL_PPP_Init() for the first time, it is mandatory to use this macro + * to set to 0 the Handle's "State" field. + * Otherwise, "State" field may have any random value and the first time the function + * HAL_PPP_Init() is called, the low level hardware initialization will be missed + * (i.e. HAL_PPP_MspInit() will not be executed). + * - When there is a need to reconfigure the low level hardware: instead of calling + * HAL_PPP_DeInit() then HAL_PPP_Init(), user can make a call to this macro then HAL_PPP_Init(). + * In this later function, when the Handle's "State" field is set to 0, it will execute the function + * HAL_PPP_MspInit() which will reconfigure the low level hardware. + * @retval None + */ +#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U) + +#if (USE_RTOS == 1) + /* Reserved for future use */ + #error "USE_RTOS should be 0 in the current HAL release" +#else + #define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0) + + #define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0) +#endif /* USE_RTOS */ + +#if defined ( __GNUC__ ) + #ifndef __weak + #define __weak __attribute__((weak)) + #endif /* __weak */ + #ifndef __packed + #define __packed __attribute__((__packed__)) + #endif /* __packed */ +#endif /* __GNUC__ */ + + +/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */ +#if defined (__GNUC__) /* GNU Compiler */ + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif /* __ALIGN_BEGIN */ +#else + #ifndef __ALIGN_END + #define __ALIGN_END + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #if defined (__CC_ARM) /* ARM Compiler */ + #define __ALIGN_BEGIN __align(4) + #elif defined (__ICCARM__) /* IAR Compiler */ + #define __ALIGN_BEGIN + #endif /* __CC_ARM */ + #endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + + +/** + * @brief __RAM_FUNC definition + */ +#if defined ( __CC_ARM ) +/* ARM Compiler + ------------ + RAM functions are defined using the toolchain options. + Functions that are executed in RAM should reside in a separate source module. + Using the 'Options for File' dialog you can simply change the 'Code / Const' + area of a module to a memory space in physical RAM. + Available memory areas are declared in the 'Target' tab of the 'Options for Target' + dialog. +*/ +#define __RAM_FUNC HAL_StatusTypeDef + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- + RAM functions are defined using a specific toolchain keyword "__ramfunc". +*/ +#define __RAM_FUNC __ramfunc HAL_StatusTypeDef + +#elif defined ( __GNUC__ ) +/* GNU Compiler + ------------ + RAM functions are defined using a specific toolchain attribute + "__attribute__((section(".RamFunc")))". +*/ +#define __RAM_FUNC HAL_StatusTypeDef __attribute__((section(".RamFunc"))) + +#endif + +/** + * @brief __NOINLINE definition + */ +#if defined ( __CC_ARM ) || defined ( __GNUC__ ) +/* ARM & GNUCompiler + ---------------- +*/ +#define __NOINLINE __attribute__ ( (noinline) ) + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- +*/ +#define __NOINLINE _Pragma("optimize = no_inline") + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ___STM32F4xx_HAL_DEF */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32f4/inc/stm32f4xx_hal_gpio_ex.h b/panda/board/stm32f4/inc/stm32f4xx_hal_gpio_ex.h new file mode 100644 index 0000000..6c5f34d --- /dev/null +++ b/panda/board/stm32f4/inc/stm32f4xx_hal_gpio_ex.h @@ -0,0 +1,1591 @@ +/** + ****************************************************************************** + * @file stm32f4xx_hal_gpio_ex.h + * @author MCD Application Team + * @version V1.6.0 + * @date 04-November-2016 + * @brief Header file of GPIO HAL Extension module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F4xx_HAL_GPIO_EX_H +#define __STM32F4xx_HAL_GPIO_EX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_hal_def.h" + +/** @addtogroup STM32F4xx_HAL_Driver + * @{ + */ + +/** @defgroup GPIOEx GPIOEx + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_Alternate_function_selection GPIO Alternate Function Selection + * @{ + */ + +/*------------------------------------------ STM32F429xx/STM32F439xx ---------*/ +#if defined(STM32F429xx) || defined(STM32F439xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_LTDC ((uint8_t)0x09U) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_LTDC ((uint8_t)0x0EU) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F429xx || STM32F439xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F427xx/STM32F437xx------------------*/ +#if defined(STM32F427xx) || defined(STM32F437xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +/** @brief GPIO_Legacy + */ +#define GPIO_AF5_I2S3ext GPIO_AF5_SPI3 /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F427xx || STM32F437xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F407xx/STM32F417xx------------------*/ +#if defined(STM32F407xx) || defined(STM32F417xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FSMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F407xx || STM32F417xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F405xx/STM32F415xx------------------*/ +#if defined(STM32F405xx) || defined(STM32F415xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FSMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F405xx || STM32F415xx */ + +/*----------------------------------------------------------------------------*/ + +/*---------------------------------------- STM32F401xx------------------------*/ +#if defined(STM32F401xC) || defined(STM32F401xE) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ + + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F401xC || STM32F401xE */ +/*----------------------------------------------------------------------------*/ + +/*--------------- STM32F412Zx/STM32F412Vx/STM32F412Rx/STM32F412Cx-------------*/ +#if defined(STM32F412Zx) || defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_DFSDM1 ((uint8_t)0x06U) /* DFSDM1 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_USART3 ((uint8_t)0x08U) /* USART3 Alternate Function mapping */ +#define GPIO_AF8_DFSDM1 ((uint8_t)0x08U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF8_CAN1 ((uint8_t)0x08U) /* CAN1 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_DFSDM1 ((uint8_t)0x0AU) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ +#define GPIO_AF10_FMC ((uint8_t)0x0AU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx */ + +/*----------------------------------------------------------------------------*/ + +/*--------------- STM32F413xx/STM32F423xx-------------------------------------*/ +#if defined(STM32F413xx) || defined(STM32F423xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ +#define GPIO_AF1_LPTIM1 ((uint8_t)0x01U) /* LPTIM1 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ +#define GPIO_AF3_DFSDM2 ((uint8_t)0x03U) /* DFSDM2 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_DFSDM1 ((uint8_t)0x06U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF6_DFSDM2 ((uint8_t)0x06U) /* DFSDM2 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_SAI1 ((uint8_t)0x07U) /* SAI1 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ +#define GPIO_AF7_DFSDM2 ((uint8_t)0x07U) /* DFSDM2 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_USART3 ((uint8_t)0x08U) /* USART3 Alternate Function mapping */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ +#define GPIO_AF8_DFSDM1 ((uint8_t)0x08U) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF8_CAN1 ((uint8_t)0x08U) /* CAN1 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_SAI1 ((uint8_t)0x0AU) /* SAI1 Alternate Function mapping */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_DFSDM1 ((uint8_t)0x0AU) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF10_DFSDM2 ((uint8_t)0x0AU) /* DFSDM2 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ +#define GPIO_AF10_FSMC ((uint8_t)0x0AU) /* FSMC Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_UART4 ((uint8_t)0x0BU) /* UART4 Alternate Function mapping */ +#define GPIO_AF11_UART5 ((uint8_t)0x0BU) /* UART5 Alternate Function mapping */ +#define GPIO_AF11_UART9 ((uint8_t)0x0BU) /* UART9 Alternate Function mapping */ +#define GPIO_AF11_UART10 ((uint8_t)0x0BU) /* UART10 Alternate Function mapping */ +#define GPIO_AF11_CAN3 ((uint8_t)0x0BU) /* CAN3 Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ +#define GPIO_AF12_FSMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_RNG ((uint8_t)0x0EU) /* RNG Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F413xx || STM32F423xx */ + +/*---------------------------------------- STM32F411xx------------------------*/ +#if defined(STM32F411xE) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4/I2S4 Alternate Function mapping */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_I2C3 ((uint8_t)0x09U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F411xE */ + +/*---------------------------------------- STM32F410xx------------------------*/ +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_LPTIM1 ((uint8_t)0x01U) /* LPTIM1 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#if defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#endif /* STM32F410Cx || STM32F410Rx */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI1 ((uint8_t)0x06U) /* SPI1 Alternate Function mapping */ +#if defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* I2S2 Alternate Function mapping */ +#endif /* STM32F410Cx || STM32F410Rx */ +#define GPIO_AF6_SPI5 ((uint8_t)0x06U) /* SPI5/I2S5 Alternate Function mapping */ +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_I2C2 ((uint8_t)0x09U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF9_FMPI2C1 ((uint8_t)0x09U) /* FMPI2C1 Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +/*---------------------------------------- STM32F446xx -----------------------*/ +#if defined(STM32F446xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ +#define GPIO_AF3_CEC ((uint8_t)0x03U) /* CEC Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_FMPI2C1 ((uint8_t)0x04U) /* FMPI2C1 Alternate Function mapping */ +#define GPIO_AF4_CEC ((uint8_t)0x04U) /* CEC Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1/I2S1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_SPI4 ((uint8_t)0x06U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_UART5 ((uint8_t)0x07U) /* UART5 Alternate Function mapping */ +#define GPIO_AF7_SPI2 ((uint8_t)0x07U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF7_SPDIFRX ((uint8_t)0x07U) /* SPDIFRX Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_SPDIFRX ((uint8_t)0x08U) /* SPDIFRX Alternate Function mapping */ +#define GPIO_AF8_SAI2 ((uint8_t)0x08U) /* SAI2 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ +#define GPIO_AF10_SAI2 ((uint8_t)0x0AU) /* SAI2 Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ + +#endif /* STM32F446xx */ +/*----------------------------------------------------------------------------*/ + +/*-------------------------------- STM32F469xx/STM32F479xx--------------------*/ +#if defined(STM32F469xx) || defined(STM32F479xx) +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00U) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00U) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_TAMPER ((uint8_t)0x00U) /* TAMPER (TAMPER_1 and TAMPER_2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00U) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00U) /* TRACE Alternate Function mapping */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01U) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01U) /* TIM2 Alternate Function mapping */ + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02U) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02U) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02U) /* TIM5 Alternate Function mapping */ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03U) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_TIM9 ((uint8_t)0x03U) /* TIM9 Alternate Function mapping */ +#define GPIO_AF3_TIM10 ((uint8_t)0x03U) /* TIM10 Alternate Function mapping */ +#define GPIO_AF3_TIM11 ((uint8_t)0x03U) /* TIM11 Alternate Function mapping */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04U) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04U) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04U) /* I2C3 Alternate Function mapping */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05U) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05U) /* SPI2/I2S2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05U) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05U) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05U) /* SPI6 Alternate Function mapping */ +#define GPIO_AF5_I2S3ext ((uint8_t)0x05U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06U) /* SPI3/I2S3 Alternate Function mapping */ +#define GPIO_AF6_I2S2ext ((uint8_t)0x06U) /* I2S2ext_SD Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06U) /* SAI1 Alternate Function mapping */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_USART1 ((uint8_t)0x07U) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07U) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07U) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_I2S3ext ((uint8_t)0x07U) /* I2S3ext_SD Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_UART4 ((uint8_t)0x08U) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08U) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_USART6 ((uint8_t)0x08U) /* USART6 Alternate Function mapping */ +#define GPIO_AF8_UART7 ((uint8_t)0x08U) /* UART7 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08U) /* UART8 Alternate Function mapping */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_CAN1 ((uint8_t)0x09U) /* CAN1 Alternate Function mapping */ +#define GPIO_AF9_CAN2 ((uint8_t)0x09U) /* CAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM12 ((uint8_t)0x09U) /* TIM12 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09U) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09U) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_LTDC ((uint8_t)0x09U) /* LCD-TFT Alternate Function mapping */ +#define GPIO_AF9_QSPI ((uint8_t)0x09U) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 10 selection + */ +#define GPIO_AF10_OTG_FS ((uint8_t)0x0AU) /* OTG_FS Alternate Function mapping */ +#define GPIO_AF10_OTG_HS ((uint8_t)0x0AU) /* OTG_HS Alternate Function mapping */ +#define GPIO_AF10_QSPI ((uint8_t)0x0AU) /* QSPI Alternate Function mapping */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_ETH ((uint8_t)0x0BU) /* ETHERNET Alternate Function mapping */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0CU) /* FMC Alternate Function mapping */ +#define GPIO_AF12_OTG_HS_FS ((uint8_t)0x0CU) /* OTG HS configured in FS, Alternate Function mapping */ +#define GPIO_AF12_SDIO ((uint8_t)0x0CU) /* SDIO Alternate Function mapping */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0DU) /* DCMI Alternate Function mapping */ +#define GPIO_AF13_DSI ((uint8_t)0x0DU) /* DSI Alternate Function mapping */ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_LTDC ((uint8_t)0x0EU) /* LCD-TFT Alternate Function mapping */ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0FU) /* EVENTOUT Alternate Function mapping */ + +#endif /* STM32F469xx || STM32F479xx */ +/*----------------------------------------------------------------------------*/ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Macros GPIO Exported Macros + * @{ + */ +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Functions GPIO Exported Functions + * @{ + */ +/** + * @} + */ + +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Constants GPIO Private Constants + * @{ + */ +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Macros GPIO Private Macros + * @{ + */ +/** @defgroup GPIOEx_Get_Port_Index GPIO Get Port Index + * @{ + */ +#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U : 8U) +#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx */ + +#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx) ||\ + defined(STM32F469xx) || defined(STM32F479xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U :\ + ((__GPIOx__) == (GPIOH))? 7U :\ + ((__GPIOx__) == (GPIOI))? 8U :\ + ((__GPIOx__) == (GPIOJ))? 9U : 10U) +#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */ + +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U : 7U) +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +#if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F411xE) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U : 7U) +#endif /* STM32F401xC || STM32F401xE || STM32F411xE */ + +#if defined(STM32F446xx) || defined(STM32F412Zx) ||defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) || defined(STM32F413xx) || defined(STM32F423xx) +#define GPIO_GET_INDEX(__GPIOx__) (uint8_t)(((__GPIOx__) == (GPIOA))? 0U :\ + ((__GPIOx__) == (GPIOB))? 1U :\ + ((__GPIOx__) == (GPIOC))? 2U :\ + ((__GPIOx__) == (GPIOD))? 3U :\ + ((__GPIOx__) == (GPIOE))? 4U :\ + ((__GPIOx__) == (GPIOF))? 5U :\ + ((__GPIOx__) == (GPIOG))? 6U : 7U) +#endif /* STM32F446xx || STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx || STM32F413xx || STM32F423xx */ + +/** + * @} + */ + +/** @defgroup GPIOEx_IS_Alternat_function_selection GPIO Check Alternate Function + * @{ + */ +/*------------------------- STM32F429xx/STM32F439xx---------------------------*/ +#if defined(STM32F429xx) || defined(STM32F439xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF14_LTDC)) + +#endif /* STM32F429xx || STM32F439xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F427xx/STM32F437xx------------------*/ +#if defined(STM32F427xx) || defined(STM32F437xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1)) + +#endif /* STM32F427xx || STM32F437xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F407xx/STM32F417xx------------------*/ +#if defined(STM32F407xx) || defined(STM32F417xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F407xx || STM32F417xx */ +/*----------------------------------------------------------------------------*/ + +/*---------------------------------- STM32F405xx/STM32F415xx------------------*/ +#if defined(STM32F405xx) || defined(STM32F415xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF12_OTG_HS_FS) || ((AF) == GPIO_AF12_SDIO) || \ + ((AF) == GPIO_AF12_FSMC) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F405xx || STM32F415xx */ + +/*----------------------------------------------------------------------------*/ + +/*---------------------------------------- STM32F401xx------------------------*/ +#if defined(STM32F401xC) || defined(STM32F401xE) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF4_I2C1) || \ + ((AF) == GPIO_AF4_I2C2) || ((AF) == GPIO_AF4_I2C3) || \ + ((AF) == GPIO_AF5_SPI1) || ((AF) == GPIO_AF5_SPI2) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF8_USART6) || ((AF) == GPIO_AF10_OTG_FS) || \ + ((AF) == GPIO_AF9_I2C2) || ((AF) == GPIO_AF9_I2C3) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F401xC || STM32F401xE */ +/*----------------------------------------------------------------------------*/ +/*---------------------------------------- STM32F410xx------------------------*/ +#if defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) +#define IS_GPIO_AF(AF) (((AF) < 10U) || ((AF) == 15U)) +#endif /* STM32F410Tx || STM32F410Cx || STM32F410Rx */ + +/*---------------------------------------- STM32F411xx------------------------*/ +#if defined(STM32F411xE) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF4_I2C1) || \ + ((AF) == GPIO_AF4_I2C2) || ((AF) == GPIO_AF4_I2C3) || \ + ((AF) == GPIO_AF5_SPI1) || ((AF) == GPIO_AF5_SPI2) || \ + ((AF) == GPIO_AF5_SPI3) || ((AF) == GPIO_AF6_SPI4) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF6_SPI5) || ((AF) == GPIO_AF7_SPI3) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF8_USART6) || ((AF) == GPIO_AF10_OTG_FS) || \ + ((AF) == GPIO_AF9_I2C2) || ((AF) == GPIO_AF9_I2C3) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF15_EVENTOUT)) + +#endif /* STM32F411xE */ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------- STM32F446xx ----------------*/ +#if defined(STM32F446xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF3_CEC) || ((AF) == GPIO_AF4_CEC) || \ + ((AF) == GPIO_AF5_SPI3) || ((AF) == GPIO_AF6_SPI2) || \ + ((AF) == GPIO_AF6_SPI4) || ((AF) == GPIO_AF7_UART5) || \ + ((AF) == GPIO_AF7_SPI2) || ((AF) == GPIO_AF7_SPI3) || \ + ((AF) == GPIO_AF7_SPDIFRX) || ((AF) == GPIO_AF8_SPDIFRX) || \ + ((AF) == GPIO_AF8_SAI2) || ((AF) == GPIO_AF9_QSPI) || \ + ((AF) == GPIO_AF10_SAI2) || ((AF) == GPIO_AF10_QSPI)) + +#endif /* STM32F446xx */ +/*----------------------------------------------------------------------------*/ + +/*------------------------------------------- STM32F469xx/STM32F479xx --------*/ +#if defined(STM32F469xx) || defined(STM32F479xx) +#define IS_GPIO_AF(AF) (((AF) == GPIO_AF0_RTC_50Hz) || ((AF) == GPIO_AF9_TIM14) || \ + ((AF) == GPIO_AF0_MCO) || ((AF) == GPIO_AF0_TAMPER) || \ + ((AF) == GPIO_AF0_SWJ) || ((AF) == GPIO_AF0_TRACE) || \ + ((AF) == GPIO_AF1_TIM1) || ((AF) == GPIO_AF1_TIM2) || \ + ((AF) == GPIO_AF2_TIM3) || ((AF) == GPIO_AF2_TIM4) || \ + ((AF) == GPIO_AF2_TIM5) || ((AF) == GPIO_AF3_TIM8) || \ + ((AF) == GPIO_AF4_I2C1) || ((AF) == GPIO_AF4_I2C2) || \ + ((AF) == GPIO_AF4_I2C3) || ((AF) == GPIO_AF5_SPI1) || \ + ((AF) == GPIO_AF5_SPI2) || ((AF) == GPIO_AF9_TIM13) || \ + ((AF) == GPIO_AF6_SPI3) || ((AF) == GPIO_AF9_TIM12) || \ + ((AF) == GPIO_AF7_USART1) || ((AF) == GPIO_AF7_USART2) || \ + ((AF) == GPIO_AF7_USART3) || ((AF) == GPIO_AF8_UART4) || \ + ((AF) == GPIO_AF8_UART5) || ((AF) == GPIO_AF8_USART6) || \ + ((AF) == GPIO_AF9_CAN1) || ((AF) == GPIO_AF9_CAN2) || \ + ((AF) == GPIO_AF10_OTG_FS) || ((AF) == GPIO_AF10_OTG_HS) || \ + ((AF) == GPIO_AF11_ETH) || ((AF) == GPIO_AF12_OTG_HS_FS) || \ + ((AF) == GPIO_AF12_SDIO) || ((AF) == GPIO_AF13_DCMI) || \ + ((AF) == GPIO_AF15_EVENTOUT) || ((AF) == GPIO_AF5_SPI4) || \ + ((AF) == GPIO_AF5_SPI5) || ((AF) == GPIO_AF5_SPI6) || \ + ((AF) == GPIO_AF8_UART7) || ((AF) == GPIO_AF8_UART8) || \ + ((AF) == GPIO_AF12_FMC) || ((AF) == GPIO_AF6_SAI1) || \ + ((AF) == GPIO_AF14_LTDC) || ((AF) == GPIO_AF13_DSI) || \ + ((AF) == GPIO_AF9_QSPI) || ((AF) == GPIO_AF10_QSPI)) + +#endif /* STM32F469xx || STM32F479xx */ +/*----------------------------------------------------------------------------*/ + +/*------------------STM32F412Zx/STM32F412Vx/STM32F412Rx/STM32F412Cx-----------*/ +#if defined(STM32F412Zx) || defined(STM32F412Vx) || defined(STM32F412Rx) || defined(STM32F412Cx) +#define IS_GPIO_AF(AF) (((AF) < 16U) && ((AF) != 11U) && ((AF) != 14U) && ((AF) != 13U)) +#endif /* STM32F412Zx || STM32F412Vx || STM32F412Rx || STM32F412Cx */ +/*----------------------------------------------------------------------------*/ + +/*------------------STM32F413xx/STM32F423xx-----------------------------------*/ +#if defined(STM32F413xx) || defined(STM32F423xx) +#define IS_GPIO_AF(AF) (((AF) < 16U) && ((AF) != 13U)) +#endif /* STM32F413xx || STM32F423xx */ +/*----------------------------------------------------------------------------*/ + +/** + * @} + */ + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F4xx_HAL_GPIO_EX_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32f4/inc/system_stm32f4xx.h b/panda/board/stm32f4/inc/system_stm32f4xx.h new file mode 100644 index 0000000..7978dab --- /dev/null +++ b/panda/board/stm32f4/inc/system_stm32f4xx.h @@ -0,0 +1,124 @@ +/** + ****************************************************************************** + * @file system_stm32f4xx.h + * @author MCD Application Team + * @version V2.6.0 + * @date 04-November-2016 + * @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f4xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef __SYSTEM_STM32F4XX_H +#define __SYSTEM_STM32F4XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32F4xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32F4xx_System_Exported_types + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + +extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */ +extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F4xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /*__SYSTEM_STM32F4XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32f4/interrupt_handlers.h b/panda/board/stm32f4/interrupt_handlers.h new file mode 100644 index 0000000..41d7427 --- /dev/null +++ b/panda/board/stm32f4/interrupt_handlers.h @@ -0,0 +1,98 @@ +// ********************* Bare interrupt handlers ********************* +// Only implemented the STM32F413 interrupts for now + +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);} +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);} diff --git a/panda/board/stm32f4/lladc.h b/panda/board/stm32f4/lladc.h new file mode 100644 index 0000000..c2df10f --- /dev/null +++ b/panda/board/stm32f4/lladc.h @@ -0,0 +1,24 @@ + +void register_set(volatile uint32_t *addr, uint32_t val, uint32_t mask); + +void adc_init(void) { + register_set(&(ADC->CCR), ADC_CCR_TSVREFE | ADC_CCR_VBATE, 0xC30000U); + register_set(&(ADC1->CR2), ADC_CR2_ADON, 0xFF7F0F03U); + register_set(&(ADC1->SMPR1), ADC_SMPR1_SMP12 | ADC_SMPR1_SMP13, 0x7FFFFFFU); +} + +uint16_t adc_get_raw(uint8_t channel) { + // Select channel + register_set(&(ADC1->JSQR), ((uint32_t) channel << 15U), 0x3FFFFFU); + + // Start conversion + ADC1->SR &= ~(ADC_SR_JEOC); + ADC1->CR2 |= ADC_CR2_JSWSTART; + while (!(ADC1->SR & ADC_SR_JEOC)); + + return ADC1->JDR1; +} + +uint16_t adc_get_mV(uint8_t channel) { + return (adc_get_raw(channel) * current_board->avdd_mV) / 4095U; +} diff --git a/panda/board/stm32f4/llbxcan.h b/panda/board/stm32f4/llbxcan.h new file mode 100644 index 0000000..72523cf --- /dev/null +++ b/panda/board/stm32f4/llbxcan.h @@ -0,0 +1,158 @@ +// Flasher and pedal use raw mailbox access +#define GET_MAILBOX_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0xFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) +#define GET_MAILBOX_BYTES_04(msg) ((msg)->RDLR) +#define GET_MAILBOX_BYTES_48(msg) ((msg)->RDHR) + +// SAE 2284-3 : minimum 16 tq, SJW 3, sample point at 81.3% +#define CAN_QUANTA 16U +#define CAN_SEQ1 12U +#define CAN_SEQ2 3U +#define CAN_SJW 3U + +#define CAN_PCLK 48000U +// 333 = 33.3 kbps +// 5000 = 500 kbps +#define can_speed_to_prescaler(x) (CAN_PCLK / CAN_QUANTA * 10U / (x)) + +#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==CAN1) ? "CAN1" : (((CAN_DEV) == CAN2) ? "CAN2" : "CAN3")) + +void print(const char *a); + +// kbps multiplied by 10 +const uint32_t speeds[] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; +const uint32_t data_speeds[] = {0U}; // No separate data speed, dummy + +bool llcan_set_speed(CAN_TypeDef *CANx, uint32_t speed, bool loopback, bool silent) { + bool ret = true; + + // initialization mode + register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_INRQ, 0x180FFU); + uint32_t timeout_counter = 0U; + while((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK){ + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if(timeout_counter >= CAN_INIT_TIMEOUT_MS){ + print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (1)!\n"); + ret = false; + break; + } + } + + if(ret){ + // set time quanta from defines + register_set(&(CANx->BTR), ((CAN_BTR_TS1_0 * (CAN_SEQ1-1U)) | + (CAN_BTR_TS2_0 * (CAN_SEQ2-1U)) | + (CAN_BTR_SJW_0 * (CAN_SJW-1U)) | + (can_speed_to_prescaler(speed) - 1U)), 0xC37F03FFU); + + // silent loopback mode for debugging + if (loopback) { + register_set_bits(&(CANx->BTR), CAN_BTR_SILM | CAN_BTR_LBKM); + } + if (silent) { + register_set_bits(&(CANx->BTR), CAN_BTR_SILM); + } + + // reset + register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU); + + timeout_counter = 0U; + while(((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if(timeout_counter >= CAN_INIT_TIMEOUT_MS){ + print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (2)!\n"); + ret = false; + break; + } + } + } + + return ret; +} + +void llcan_irq_disable(const CAN_TypeDef *CANx) { + if (CANx == CAN1) { + NVIC_DisableIRQ(CAN1_TX_IRQn); + NVIC_DisableIRQ(CAN1_RX0_IRQn); + NVIC_DisableIRQ(CAN1_SCE_IRQn); + } else if (CANx == CAN2) { + NVIC_DisableIRQ(CAN2_TX_IRQn); + NVIC_DisableIRQ(CAN2_RX0_IRQn); + NVIC_DisableIRQ(CAN2_SCE_IRQn); + } else if (CANx == CAN3) { + NVIC_DisableIRQ(CAN3_TX_IRQn); + NVIC_DisableIRQ(CAN3_RX0_IRQn); + NVIC_DisableIRQ(CAN3_SCE_IRQn); + } else { + } +} + +void llcan_irq_enable(const CAN_TypeDef *CANx) { + if (CANx == CAN1) { + NVIC_EnableIRQ(CAN1_TX_IRQn); + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_SCE_IRQn); + } else if (CANx == CAN2) { + NVIC_EnableIRQ(CAN2_TX_IRQn); + NVIC_EnableIRQ(CAN2_RX0_IRQn); + NVIC_EnableIRQ(CAN2_SCE_IRQn); + } else if (CANx == CAN3) { + NVIC_EnableIRQ(CAN3_TX_IRQn); + NVIC_EnableIRQ(CAN3_RX0_IRQn); + NVIC_EnableIRQ(CAN3_SCE_IRQn); + } else { + } +} + +bool llcan_init(CAN_TypeDef *CANx) { + bool ret = true; + + // Enter init mode + register_set_bits(&(CANx->FMR), CAN_FMR_FINIT); + + // Wait for INAK bit to be set + uint32_t timeout_counter = 0U; + while(((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if(timeout_counter >= CAN_INIT_TIMEOUT_MS){ + print(CAN_NAME_FROM_CANIF(CANx)); print(" initialization timed out!\n"); + ret = false; + break; + } + } + + if(ret){ + // no mask + // For some weird reason some of these registers do not want to set properly on CAN2 and CAN3. Probably something to do with the single/dual mode and their different filters. + CANx->sFilterRegister[0].FR1 = 0U; + CANx->sFilterRegister[0].FR2 = 0U; + CANx->sFilterRegister[14].FR1 = 0U; + CANx->sFilterRegister[14].FR2 = 0U; + CANx->FA1R |= 1U | (1UL << 14); + + // Exit init mode, do not wait + register_clear_bits(&(CANx->FMR), CAN_FMR_FINIT); + + // enable certain CAN interrupts + register_set_bits(&(CANx->IER), CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_ERRIE | CAN_IER_LECIE | CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | CAN_IER_FOVIE0 | CAN_IER_FFIE0); + + // clear overrun flag on init + CANx->RF0R &= ~(CAN_RF0R_FOVR0); + + llcan_irq_enable(CANx); + } + return ret; +} + +void llcan_clear_send(CAN_TypeDef *CANx) { + CANx->TSR |= CAN_TSR_ABRQ0; // Abort message transmission on error interrupt + CANx->MSR |= CAN_MSR_ERRI; // Clear error interrupt +} diff --git a/panda/board/stm32f4/llexti.h b/panda/board/stm32f4/llexti.h new file mode 100644 index 0000000..6de13ab --- /dev/null +++ b/panda/board/stm32f4/llexti.h @@ -0,0 +1,56 @@ +void EXTI_IRQ_Handler(void); + +void exti_irq_init(void) { + SYSCFG->EXTICR[2] &= ~(SYSCFG_EXTICR3_EXTI8_Msk); + if (harness.status == HARNESS_STATUS_FLIPPED) { + // CAN2_RX + current_board->enable_can_transceiver(3U, false); + SYSCFG->EXTICR[2] |= (SYSCFG_EXTICR3_EXTI8_PA); + + // IRQ on falling edge for PC3 (SBU2, EXTI3) + SYSCFG->EXTICR[0] &= ~(SYSCFG_EXTICR1_EXTI3_Msk); + SYSCFG->EXTICR[0] |= (SYSCFG_EXTICR1_EXTI3_PC); + EXTI->IMR |= EXTI_IMR_MR3; + EXTI->RTSR &= ~EXTI_RTSR_TR3; // rising edge + EXTI->FTSR |= EXTI_FTSR_TR3; // falling edge + REGISTER_INTERRUPT(EXTI3_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI3_IRQn); + } else { + // CAN0_RX + current_board->enable_can_transceiver(1U, false); + SYSCFG->EXTICR[2] |= (SYSCFG_EXTICR3_EXTI8_PB); + + // IRQ on falling edge for PC0 (SBU1, EXTI0) + SYSCFG->EXTICR[0] &= ~(SYSCFG_EXTICR1_EXTI0_Msk); + SYSCFG->EXTICR[0] |= (SYSCFG_EXTICR1_EXTI0_PC); + EXTI->IMR |= EXTI_IMR_MR0; + EXTI->RTSR &= ~EXTI_RTSR_TR0; // rising edge + EXTI->FTSR |= EXTI_FTSR_TR0; // falling edge + REGISTER_INTERRUPT(EXTI0_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI0_IRQn); + } + // CAN0 or CAN2 IRQ on falling edge (EXTI8) + EXTI->IMR |= EXTI_IMR_MR8; + EXTI->RTSR &= ~EXTI_RTSR_TR8; // rising edge + EXTI->FTSR |= EXTI_FTSR_TR8; // falling edge + REGISTER_INTERRUPT(EXTI9_5_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI9_5_IRQn); +} + +bool check_exti_irq(void) { + return ((EXTI->PR & EXTI_PR_PR8) || (EXTI->PR & EXTI_PR_PR3) || (EXTI->PR & EXTI_PR_PR0)); +} + +void exti_irq_clear(void) { + // Clear pending bits + EXTI->PR |= EXTI_PR_PR8; + EXTI->PR |= EXTI_PR_PR0; + EXTI->PR |= EXTI_PR_PR3; + EXTI->PR |= EXTI_PR_PR22; + + // Disable all active EXTI IRQs + EXTI->IMR &= ~EXTI_IMR_MR8; + EXTI->IMR &= ~EXTI_IMR_MR0; + EXTI->IMR &= ~EXTI_IMR_MR3; + EXTI->IMR &= ~EXTI_IMR_MR22; +} diff --git a/panda/board/stm32f4/llfan.h b/panda/board/stm32f4/llfan.h new file mode 100644 index 0000000..9e3f0c6 --- /dev/null +++ b/panda/board/stm32f4/llfan.h @@ -0,0 +1,23 @@ +// TACH interrupt handler +void EXTI2_IRQ_Handler(void) { + volatile unsigned int pr = EXTI->PR & (1U << 2); + if ((pr & (1U << 2)) != 0U) { + fan_state.tach_counter++; + } + EXTI->PR = (1U << 2); +} + +void llfan_init(void) { + // 5000RPM * 4 tach edges / 60 seconds + REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 700U, FAULT_INTERRUPT_RATE_TACH) + + // Init PWM speed control + pwm_init(TIM3, 3); + + // Init TACH interrupt + register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U); + register_set_bits(&(EXTI->IMR), (1U << 2)); + register_set_bits(&(EXTI->RTSR), (1U << 2)); + register_set_bits(&(EXTI->FTSR), (1U << 2)); + NVIC_EnableIRQ(EXTI2_IRQn); +} diff --git a/panda/board/stm32f4/llflash.h b/panda/board/stm32f4/llflash.h new file mode 100644 index 0000000..61adcd4 --- /dev/null +++ b/panda/board/stm32f4/llflash.h @@ -0,0 +1,28 @@ +bool flash_is_locked(void) { + return (FLASH->CR & FLASH_CR_LOCK); +} + +void flash_unlock(void) { + FLASH->KEYR = 0x45670123; + FLASH->KEYR = 0xCDEF89AB; +} + +bool flash_erase_sector(uint8_t sector, bool unlocked) { + // don't erase the bootloader(sector 0) + if (sector != 0 && sector < 12 && unlocked) { + FLASH->CR = (sector << 3) | FLASH_CR_SER; + FLASH->CR |= FLASH_CR_STRT; + while (FLASH->SR & FLASH_SR_BSY); + return true; + } + return false; +} + +void flash_write_word(void *prog_ptr, uint32_t data) { + uint32_t *pp = prog_ptr; + FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG; + *pp = data; + while (FLASH->SR & FLASH_SR_BSY); +} + +void flush_write_buffer(void) { } diff --git a/panda/board/stm32f4/llspi.h b/panda/board/stm32f4/llspi.h new file mode 100644 index 0000000..31e419b --- /dev/null +++ b/panda/board/stm32f4/llspi.h @@ -0,0 +1,90 @@ +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); + + // setup source and length + register_set(&(DMA2_Stream3->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream3->NDTR = len; + + // enable DMA + register_set_bits(&(SPI1->CR2), SPI_CR2_TXDMAEN); + DMA2_Stream3->CR |= DMA_SxCR_EN; +} + +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; + + // drain the bus + volatile uint8_t dat = SPI1->DR; + (void)dat; + + // setup destination and length + register_set(&(DMA2_Stream2->M0AR), (uint32_t)addr, 0xFFFFFFFFU); + DMA2_Stream2->NDTR = len; + + // enable DMA + DMA2_Stream2->CR |= DMA_SxCR_EN; + register_set_bits(&(SPI1->CR2), SPI_CR2_RXDMAEN); +} + +// SPI MOSI DMA FINISHED +void DMA2_Stream2_IRQ_Handler(void) { + // Clear interrupt flag + ENTER_CRITICAL(); + DMA2->LIFCR = DMA_LIFCR_CTCIF2; + + spi_rx_done(); + + EXIT_CRITICAL(); +} + +// SPI MISO 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 + // Timeout to prevent hang when the master clock stops. + bool timed_out = false; + uint32_t start_time = microsecond_timer_get(); + while (!(SPI1->SR & SPI_SR_TXE)) { + if (get_ts_elapsed(microsecond_timer_get(), start_time) > SPI_TIMEOUT_US) { + timed_out = true; + break; + } + } + volatile uint8_t dat = SPI1->DR; + (void)dat; + SPI1->DR = 0U; + + if (timed_out) { + print("SPI: TX timeout\n"); + } + + spi_tx_done(timed_out); +} + +// ***************************** SPI init ***************************** +void llspi_init(void) { + REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) + REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) + + // 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); + + // Setup MISO DMA + register_set(&(DMA2_Stream3->CR), (DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0 | DMA_SxCR_MINC | DMA_SxCR_DIR_0 | DMA_SxCR_TCIE), 0x1E077EFEU); + register_set(&(DMA2_Stream3->PAR), (uint32_t)&(SPI1->DR), 0xFFFFFFFFU); + + // Enable SPI and the error interrupts + // TODO: verify clock phase and polarity + register_set(&(SPI1->CR1), SPI_CR1_SPE, 0xFFFFU); + register_set(&(SPI1->CR2), 0U, 0xF7U); + + NVIC_EnableIRQ(DMA2_Stream2_IRQn); + NVIC_EnableIRQ(DMA2_Stream3_IRQn); +} diff --git a/panda/board/stm32f4/lluart.h b/panda/board/stm32f4/lluart.h new file mode 100644 index 0000000..ffe7e74 --- /dev/null +++ b/panda/board/stm32f4/lluart.h @@ -0,0 +1,92 @@ +// ***************************** Interrupt handlers ***************************** + +void uart_tx_ring(uart_ring *q){ + ENTER_CRITICAL(); + // Send out next byte of TX buffer + if (q->w_ptr_tx != q->r_ptr_tx) { + // Only send if transmit register is empty (aka last byte has been sent) + if ((q->uart->SR & USART_SR_TXE) != 0) { + q->uart->DR = q->elems_tx[q->r_ptr_tx]; // This clears TXE + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + // Enable TXE interrupt if there is still data to be sent + if(q->r_ptr_tx != q->w_ptr_tx){ + q->uart->CR1 |= USART_CR1_TXEIE; + } else { + q->uart->CR1 &= ~USART_CR1_TXEIE; + } + } + EXIT_CRITICAL(); +} + +void uart_rx_ring(uart_ring *q){ + ENTER_CRITICAL(); + + // Read out RX buffer + uint8_t c = q->uart->DR; // This read after reading SR clears a bunch of interrupts + + uint16_t next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + // Do not overwrite buffer data + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = c; + q->w_ptr_rx = next_w_ptr; + if (q->callback != NULL) { + q->callback(q); + } + } + + EXIT_CRITICAL(); +} + +void uart_send_break(uart_ring *u) { + while ((u->uart->CR1 & USART_CR1_SBK) != 0); + u->uart->CR1 |= USART_CR1_SBK; +} + +// This read after reading SR clears all error interrupts. We don't want compiler warnings, nor optimizations +#define UART_READ_DR(uart) volatile uint8_t t = (uart)->DR; UNUSED(t); + +void uart_interrupt_handler(uart_ring *q) { + ENTER_CRITICAL(); + + // Read UART status. This is also the first step necessary in clearing most interrupts + uint32_t status = q->uart->SR; + + // If RXNE is set, perform a read. This clears RXNE, ORE, IDLE, NF and FE + if((status & USART_SR_RXNE) != 0U){ + uart_rx_ring(q); + } + + // Detect errors and clear them + uint32_t err = (status & USART_SR_ORE) | (status & USART_SR_NE) | (status & USART_SR_FE) | (status & USART_SR_PE); + if(err != 0U){ + #ifdef DEBUG_UART + print("Encountered UART error: "); puth(err); print("\n"); + #endif + UART_READ_DR(q->uart) + } + // Send if necessary + uart_tx_ring(q); + + EXIT_CRITICAL(); +} + +void USART2_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_debug); } + +// ***************************** Hardware setup ***************************** + +#define DIV_(_PCLK_, _BAUD_) (((_PCLK_) * 25U) / (4U * (_BAUD_))) +#define DIVMANT_(_PCLK_, _BAUD_) (DIV_((_PCLK_), (_BAUD_)) / 100U) +#define DIVFRAQ_(_PCLK_, _BAUD_) ((((DIV_((_PCLK_), (_BAUD_)) - (DIVMANT_((_PCLK_), (_BAUD_)) * 100U)) * 16U) + 50U) / 100U) +#define USART_BRR_(_PCLK_, _BAUD_) ((DIVMANT_((_PCLK_), (_BAUD_)) << 4) | (DIVFRAQ_((_PCLK_), (_BAUD_)) & 0x0FU)) + +void uart_set_baud(USART_TypeDef *u, unsigned int baud) { + u->BRR = USART_BRR_(APB1_FREQ*1000000U, baud); +} diff --git a/panda/board/stm32f4/llusb.h b/panda/board/stm32f4/llusb.h new file mode 100644 index 0000000..20c9808 --- /dev/null +++ b/panda/board/stm32f4/llusb.h @@ -0,0 +1,91 @@ +USB_OTG_GlobalTypeDef *USBx = USB_OTG_FS; + +#define USBx_HOST ((USB_OTG_HostTypeDef *)((uint32_t)USBx + USB_OTG_HOST_BASE)) +#define USBx_DEVICE ((USB_OTG_DeviceTypeDef *)((uint32_t)USBx + USB_OTG_DEVICE_BASE)) +#define USBx_INEP(i) ((USB_OTG_INEndpointTypeDef *)((uint32_t)USBx + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USBx + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_DFIFO(i) *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE)) +#define USBx_PCGCCTL *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_PCGCCTL_BASE) + +#define USBD_FS_TRDT_VALUE 5UL +#define USB_OTG_SPEED_FULL 3 + + +void usb_irqhandler(void); + +void OTG_FS_IRQ_Handler(void) { + NVIC_DisableIRQ(OTG_FS_IRQn); + //__disable_irq(); + usb_irqhandler(); + //__enable_irq(); + NVIC_EnableIRQ(OTG_FS_IRQn); +} + +void usb_init(void) { + REGISTER_INTERRUPT(OTG_FS_IRQn, OTG_FS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) //TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate + + // full speed PHY, do reset and remove power down + /*puth(USBx->GRSTCTL); + print(" resetting PHY\n");*/ + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); + //print("AHB idle\n"); + + // reset PHY here + USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST); + //print("reset done\n"); + + // internal PHY, force device mode + USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_FDMOD; + + // slowest timings + USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); + + // power up the PHY + USBx->GCCFG = USB_OTG_GCCFG_PWRDWN; + + //USBx->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_SDEN |USB_OTG_GCCFG_PDEN | USB_OTG_GCCFG_DCDEN; + + /* B-peripheral session valid override enable*/ + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; + + // be a device, slowest timings + //USBx->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL; + //USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT); + //USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL; + + // **** for debugging, doesn't seem to work **** + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_CTXPKT; + + // reset PHY clock + USBx_PCGCCTL = 0; + + // enable the fancy OTG things + // DCFG_FRAME_INTERVAL_80 is 0 + //USBx->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP; + USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK; + + //USBx_DEVICE->DCFG = USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD; + //USBx_DEVICE->DCFG = USB_OTG_DCFG_DSPD; + + // clear pending interrupts + USBx->GINTSTS = 0xBFFFFFFFU; + + // setup USB interrupts + // all interrupts except TXFIFO EMPTY + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF); + //USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM); + 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 | USB_OTG_GINTMSK_USBSUSPM | + USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM | USB_OTG_GINTMSK_EOPFM; + + USBx->GAHBCFG = USB_OTG_GAHBCFG_GINT; + + // DCTL startup value is 2 on new chip, 0 on old chip + USBx_DEVICE->DCTL = 0; + + // enable the IRQ + NVIC_EnableIRQ(OTG_FS_IRQn); +} diff --git a/panda/board/stm32f4/peripherals.h b/panda/board/stm32f4/peripherals.h new file mode 100644 index 0000000..79ac3c6 --- /dev/null +++ b/panda/board/stm32f4/peripherals.h @@ -0,0 +1,91 @@ +void gpio_usb_init(void) { + // A11,A12: USB + set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS); + set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS); + GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12; +} + +void gpio_spi_init(void) { + // A4-A7: SPI + set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1); + set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1); + register_set_bits(&(GPIOA->OSPEEDR), GPIO_OSPEEDER_OSPEEDR4 | GPIO_OSPEEDER_OSPEEDR5 | GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7); +} + +void gpio_usart2_init(void) { + // A2,A3: USART 2 for debugging + set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); + set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); +} + +// Common GPIO initialization +void common_init_gpio(void) { + // TODO: Is this block actually doing something??? + // pull low to hold ESP in reset?? + // enable OTG out tied to ground + GPIOA->ODR = 0; + GPIOB->ODR = 0; + GPIOA->PUPDR = 0; + GPIOB->AFR[0] = 0; + GPIOB->AFR[1] = 0; + + // C2: Voltage sense line + set_gpio_mode(GPIOC, 2, MODE_ANALOG); + + gpio_usb_init(); + + // B8,B9: CAN 1 + set_gpio_alternate(GPIOB, 8, GPIO_AF8_CAN1); + set_gpio_alternate(GPIOB, 9, GPIO_AF8_CAN1); +} + +void flasher_peripherals_init(void) { + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; + RCC->APB1ENR |= RCC_APB1ENR_USART2EN; +} + +// Peripheral initialization +void peripherals_init(void) { + // enable GPIO(A,B,C,D) + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; + + // Supplemental + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; + RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; + + // Connectivity + RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; + RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; + RCC->APB1ENR |= RCC_APB1ENR_USART2EN; + RCC->APB1ENR |= RCC_APB1ENR_USART3EN; + RCC->APB1ENR |= RCC_APB1ENR_UART5EN; + RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; + RCC->APB1ENR |= RCC_APB1ENR_CAN2EN; + RCC->APB1ENR |= RCC_APB1ENR_CAN3EN; + + // Analog + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + RCC->APB1ENR |= RCC_APB1ENR_DACEN; + + // Timers + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer + RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // main counter + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // pedal and fan PWM + RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // IR PWM + RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; // k-line init + RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // interrupt timer + RCC->APB2ENR |= RCC_APB2ENR_TIM9EN; // slow loop + RCC->APB1ENR |= RCC_APB1ENR_TIM12EN; // gmlan_alt +} + +void enable_interrupt_timer(void) { + register_set_bits(&(RCC->APB1ENR), RCC_APB1ENR_TIM6EN); // Enable interrupt timer peripheral +} diff --git a/panda/board/stm32fx/startup_stm32f413xx.s b/panda/board/stm32f4/startup_stm32f413xx.s similarity index 100% rename from panda/board/stm32fx/startup_stm32f413xx.s rename to panda/board/stm32f4/startup_stm32f413xx.s diff --git a/panda/board/stm32f4/stm32f4_config.h b/panda/board/stm32f4/stm32f4_config.h new file mode 100644 index 0000000..3c6e547 --- /dev/null +++ b/panda/board/stm32f4/stm32f4_config.h @@ -0,0 +1,86 @@ +#include "stm32f4/inc/stm32f4xx.h" +#include "stm32f4/inc/stm32f4xx_hal_gpio_ex.h" +#define MCU_IDCODE 0x463U + +// from the linker script +#define APP_START_ADDRESS 0x8004000U + +#define CORE_FREQ 96U // in MHz +#define APB1_FREQ (CORE_FREQ/2U) +#define APB1_TIMER_FREQ (APB1_FREQ*2U) // APB1 is multiplied by 2 for the timer peripherals +#define APB2_FREQ (CORE_FREQ/2U) +#define APB2_TIMER_FREQ (APB2_FREQ*2U) // APB2 is multiplied by 2 for the timer peripherals + +#define BOOTLOADER_ADDRESS 0x1FFF0004U + +// Around (1Mbps / 8 bits/byte / 12 bytes per message) +#define CAN_INTERRUPT_RATE 12000U + +#define MAX_LED_FADE 8192U + +#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h) + +#define TICK_TIMER_IRQ TIM1_BRK_TIM9_IRQn +#define TICK_TIMER TIM9 + +#define MICROSECOND_TIMER TIM2 + +#define INTERRUPT_TIMER_IRQ TIM6_DAC_IRQn +#define INTERRUPT_TIMER TIM6 + +#define IND_WDG IWDG + +#define PROVISION_CHUNK_ADDRESS 0x1FFF79E0U +#define DEVICE_SERIAL_NUMBER_ADDRESS 0x1FFF79C0U + +#include "can_definitions.h" +#include "comms_definitions.h" + +#ifndef BOOTSTUB + #include "main_declarations.h" +#else + #include "bootstub_declarations.h" +#endif + +#include "libc.h" +#include "critical.h" +#include "faults.h" +#include "utils.h" + +#include "drivers/registers.h" +#include "drivers/interrupts.h" +#include "drivers/gpio.h" +#include "stm32f4/peripherals.h" +#include "stm32f4/interrupt_handlers.h" +#include "drivers/timers.h" +#include "stm32f4/board.h" +#include "stm32f4/clock.h" +#include "drivers/watchdog.h" + +#include "drivers/spi.h" +#include "stm32f4/llspi.h" + +#if !defined(BOOTSTUB) + #include "drivers/uart.h" + #include "stm32f4/lluart.h" +#endif + +#if defined(PANDA) && !defined(BOOTSTUB) + #include "stm32f4/llexti.h" +#endif + +#ifdef BOOTSTUB + #include "stm32f4/llflash.h" +#else + #include "stm32f4/llbxcan.h" +#endif + +#include "stm32f4/llusb.h" + +void early_gpio_float(void) { + RCC->AHB1ENR = RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN; + + GPIOA->MODER = 0; GPIOB->MODER = 0; GPIOC->MODER = 0; + GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; + GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; +} diff --git a/panda/board/stm32fx/stm32f4_flash.ld b/panda/board/stm32f4/stm32f4_flash.ld similarity index 100% rename from panda/board/stm32fx/stm32f4_flash.ld rename to panda/board/stm32f4/stm32f4_flash.ld diff --git a/panda/board/stm32h7/board.h b/panda/board/stm32h7/board.h new file mode 100644 index 0000000..f5a8e55 --- /dev/null +++ b/panda/board/stm32h7/board.h @@ -0,0 +1,53 @@ +// ///////////////////////////////////////////////////////////// // +// Hardware abstraction layer for all different supported boards // +// ///////////////////////////////////////////////////////////// // +#include "boards/board_declarations.h" +#include "boards/unused_funcs.h" + +// ///// Board definition and detection ///// // +#include "stm32h7/lladc.h" +#include "drivers/harness.h" +#include "drivers/fan.h" +#include "stm32h7/llfan.h" +#include "stm32h7/lldac.h" +#include "drivers/fake_siren.h" +#include "drivers/clock_source.h" +#include "boards/red.h" +#include "boards/red_chiplet.h" +#include "boards/tres.h" +#include "boards/cuatro.h" + + +uint8_t get_board_id(void) { + return detect_with_pull(GPIOF, 7, PULL_UP) | + (detect_with_pull(GPIOF, 8, PULL_UP) << 1U) | + (detect_with_pull(GPIOF, 9, PULL_UP) << 2U) | + (detect_with_pull(GPIOF, 10, PULL_UP) << 3U); +} + +void detect_board_type(void) { + const uint8_t board_id = get_board_id(); + + if (board_id == 0U) { + hw_type = HW_TYPE_RED_PANDA; + current_board = &board_red; + } else if (board_id == 1U) { + // deprecated + //hw_type = HW_TYPE_RED_PANDA_V2; + } else if (board_id == 2U) { + hw_type = HW_TYPE_TRES; + current_board = &board_tres; + } else if (board_id == 3U) { + hw_type = HW_TYPE_CUATRO; + current_board = &board_tres; + } else { + hw_type = HW_TYPE_UNKNOWN; + print("Hardware type is UNKNOWN!\n"); + } + + // TODO: detect this live +#ifdef STM32H723 + hw_type = HW_TYPE_CUATRO; + current_board = &board_cuatro; +#endif +} diff --git a/panda/board/stm32h7/clock.h b/panda/board/stm32h7/clock.h new file mode 100644 index 0000000..94e08ca --- /dev/null +++ b/panda/board/stm32h7/clock.h @@ -0,0 +1,76 @@ +/* +HSE: 25MHz +PLL1Q: 80MHz (for FDCAN) +HSI48 enabled (for USB) +CPU: 240MHz +CPU Systick: 240MHz +AXI: 120MHz +HCLK3: 60MHz +APB3 per: 60MHz +AHB1,2 per: 120MHz +APB1 per: 60MHz +APB1 tim: 120MHz +APB2 per: 60MHz +APB2 tim: 120MHz +AHB4 per: 120MHz +APB4 per: 60MHz +PCLK1: 60MHz (for USART2,3,4,5,7,8) +*/ + +void clock_init(void) { + // Set power mode to direct SMPS power supply(depends on the board layout) +#ifndef STM32H723 + register_set(&(PWR->CR3), PWR_CR3_SMPSEN, 0xFU); // powered only by SMPS +#else + register_set(&(PWR->CR3), PWR_CR3_LDOEN, 0xFU); +#endif + // Set VOS level (VOS3 to 170Mhz, VOS2 to 300Mhz, VOS1 to 400Mhz, VOS0 to 550Mhz) + register_set(&(PWR->D3CR), PWR_D3CR_VOS_1 | PWR_D3CR_VOS_0, 0xC000U); //VOS1, needed for 80Mhz CAN FD + while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0); + while ((PWR->CSR1 & PWR_CSR1_ACTVOS) != (PWR->D3CR & PWR_D3CR_VOS)); // check that VOS level was actually set + + // Configure Flash ACR register LATENCY and WRHIGHFREQ (VOS0 range!) + register_set(&(FLASH->ACR), FLASH_ACR_LATENCY_2WS | 0x20U, 0x3FU); // VOS2, AXI 100MHz-150MHz + // enable external oscillator HSE + register_set_bits(&(RCC->CR), RCC_CR_HSEON); + while ((RCC->CR & RCC_CR_HSERDY) == 0); + // enable internal HSI48 for USB FS kernel + register_set_bits(&(RCC->CR), RCC_CR_HSI48ON); + while ((RCC->CR & RCC_CR_HSI48RDY) == 0); + // Specify the frequency source for PLL1, divider for DIVM1, DIVM2, DIVM3 : HSE, 5, 5, 5 + register_set(&(RCC->PLLCKSELR), RCC_PLLCKSELR_PLLSRC_HSE | RCC_PLLCKSELR_DIVM1_0 | RCC_PLLCKSELR_DIVM1_2 | RCC_PLLCKSELR_DIVM2_0 | RCC_PLLCKSELR_DIVM2_2 | RCC_PLLCKSELR_DIVM3_0 | RCC_PLLCKSELR_DIVM3_2, 0x3F3F3F3U); + + // *** PLL1 start *** + // Specify multiplier N and dividers P, Q, R for PLL1 : 48, 1, 3, 2 (clock 240Mhz, PLL1Q 80Mhz for CAN FD) + register_set(&(RCC->PLL1DIVR), 0x102002FU, 0x7F7FFFFFU); + // Specify the input and output frequency ranges, enable dividers for PLL1 + register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLL1RGE_2 | RCC_PLLCFGR_DIVP1EN | RCC_PLLCFGR_DIVQ1EN | RCC_PLLCFGR_DIVR1EN, 0x7000CU); + // Enable PLL1 + register_set_bits(&(RCC->CR), RCC_CR_PLL1ON); + while((RCC->CR & RCC_CR_PLL1RDY) == 0); + // *** PLL1 end *** + + //////////////OTHER CLOCKS//////////////////// + // RCC HCLK Clock Source / RCC APB3 Clock Source / RCC SYS Clock Source + register_set(&(RCC->D1CFGR), RCC_D1CFGR_HPRE_DIV2 | RCC_D1CFGR_D1PPRE_DIV2 | RCC_D1CFGR_D1CPRE_DIV1, 0xF7FU); + // RCC APB1 Clock Source / RCC APB2 Clock Source + register_set(&(RCC->D2CFGR), RCC_D2CFGR_D2PPRE1_DIV2 | RCC_D2CFGR_D2PPRE2_DIV2, 0x770U); + // RCC APB4 Clock Source + register_set(&(RCC->D3CFGR), RCC_D3CFGR_D3PPRE_DIV2, 0x70U); + + // Set SysClock source to PLL + register_set(&(RCC->CFGR), RCC_CFGR_SW_PLL1, 0x7U); + while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1); + //////////////END OTHER CLOCKS//////////////////// + + // Configure clock source for USB (HSI48) + register_set(&(RCC->D2CCIP2R), RCC_D2CCIP2R_USBSEL_1 | RCC_D2CCIP2R_USBSEL_0, RCC_D2CCIP2R_USBSEL); + // Configure clock source for FDCAN (PLL1Q at 80Mhz) + register_set(&(RCC->D2CCIP1R), RCC_D2CCIP1R_FDCANSEL_0, RCC_D2CCIP1R_FDCANSEL); + // Configure clock source for ADC1,2,3 (per_ck(currently HSE)) + register_set(&(RCC->D3CCIPR), RCC_D3CCIPR_ADCSEL_1, RCC_D3CCIPR_ADCSEL); + //Enable the Clock Security System + register_set_bits(&(RCC->CR), RCC_CR_CSSHSEON); + //Enable Vdd33usb supply level detector + register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); +} diff --git a/panda/board/stm32h7/inc/cmsis_compiler.h b/panda/board/stm32h7/inc/cmsis_compiler.h new file mode 100644 index 0000000..d0f39ee --- /dev/null +++ b/panda/board/stm32h7/inc/cmsis_compiler.h @@ -0,0 +1,284 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.1.0 + * @date 09. October 2018 + ******************************************************************************/ +/* + * Copyright (c) 2009-2018 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6.6 LTM (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100) + #include "cmsis_armclang_ltm.h" + + /* + * Arm Compiler above 6.10.1 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) + #include "cmsis_armclang.h" + + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #define __RESTRICT __restrict + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + + diff --git a/panda/board/stm32h7/inc/cmsis_gcc.h b/panda/board/stm32h7/inc/cmsis_gcc.h new file mode 100644 index 0000000..2f68473 --- /dev/null +++ b/panda/board/stm32h7/inc/cmsis_gcc.h @@ -0,0 +1,2169 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.2.0 + * @date 08. May 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START + +/** + \brief Initializes data and bss sections + \details This default implementations initialized all data and additional bss + sections relying on .copy.table and .zero.table specified properly + in the used linker script. + + */ +__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void) +{ + extern void _start(void) __NO_RETURN; + + typedef struct { + uint32_t const* src; + uint32_t* dest; + uint32_t wlen; + } __copy_table_t; + + typedef struct { + uint32_t* dest; + uint32_t wlen; + } __zero_table_t; + + extern const __copy_table_t __copy_table_start__; + extern const __copy_table_t __copy_table_end__; + extern const __zero_table_t __zero_table_start__; + extern const __zero_table_t __zero_table_end__; + + for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = pTable->src[i]; + } + } + + for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = 0u; + } + } + + _start(); +} + +#define __PROGRAM_START __cmsis_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __StackTop +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __StackLimit +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute((used, section(".vectors"))) +#endif + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) :: "memory"); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) :: "memory"); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ + __extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#if 0 +#define __PKHBT(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) +#endif + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ + diff --git a/panda/board/stm32h7/inc/cmsis_version.h b/panda/board/stm32h7/inc/cmsis_version.h new file mode 100644 index 0000000..bf57cf3 --- /dev/null +++ b/panda/board/stm32h7/inc/cmsis_version.h @@ -0,0 +1,40 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.3 + * @date 24. June 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 3U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif + diff --git a/panda/board/stm32h7/inc/core_cm7.h b/panda/board/stm32h7/inc/core_cm7.h new file mode 100644 index 0000000..3da3c43 --- /dev/null +++ b/panda/board/stm32h7/inc/core_cm7.h @@ -0,0 +1,2725 @@ +/**************************************************************************//** + * @file core_cm7.h + * @brief CMSIS Cortex-M7 Core Peripheral Access Layer Header File + * @version V5.1.1 + * @date 28. March 2019 + ******************************************************************************/ +/* + * Copyright (c) 2009-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM7_H_GENERIC +#define __CORE_CM7_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M7 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM7 definitions */ +#define __CM7_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM7_CMSIS_VERSION_SUB ( __CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM7_CMSIS_VERSION ((__CM7_CMSIS_VERSION_MAIN << 16U) | \ + __CM7_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (7U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_FP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM7_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM7_H_DEPENDANT +#define __CORE_CM7_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM7_REV + #define __CM7_REV 0x0000U + #warning "__CM7_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __ICACHE_PRESENT + #define __ICACHE_PRESENT 0U + #warning "__ICACHE_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DCACHE_PRESENT + #define __DCACHE_PRESENT 0U + #warning "__DCACHE_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __DTCM_PRESENT + #define __DTCM_PRESENT 0U + #warning "__DTCM_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M7 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RESERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHPR[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t ID_PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t ID_DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ID_AFR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t ID_MFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ID_ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[1U]; + __IM uint32_t CLIDR; /*!< Offset: 0x078 (R/ ) Cache Level ID register */ + __IM uint32_t CTR; /*!< Offset: 0x07C (R/ ) Cache Type register */ + __IM uint32_t CCSIDR; /*!< Offset: 0x080 (R/ ) Cache Size ID Register */ + __IOM uint32_t CSSELR; /*!< Offset: 0x084 (R/W) Cache Size Selection Register */ + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ + uint32_t RESERVED3[93U]; + __OM uint32_t STIR; /*!< Offset: 0x200 ( /W) Software Triggered Interrupt Register */ + uint32_t RESERVED4[15U]; + __IM uint32_t MVFR0; /*!< Offset: 0x240 (R/ ) Media and VFP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x244 (R/ ) Media and VFP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x248 (R/ ) Media and VFP Feature Register 2 */ + uint32_t RESERVED5[1U]; + __OM uint32_t ICIALLU; /*!< Offset: 0x250 ( /W) I-Cache Invalidate All to PoU */ + uint32_t RESERVED6[1U]; + __OM uint32_t ICIMVAU; /*!< Offset: 0x258 ( /W) I-Cache Invalidate by MVA to PoU */ + __OM uint32_t DCIMVAC; /*!< Offset: 0x25C ( /W) D-Cache Invalidate by MVA to PoC */ + __OM uint32_t DCISW; /*!< Offset: 0x260 ( /W) D-Cache Invalidate by Set-way */ + __OM uint32_t DCCMVAU; /*!< Offset: 0x264 ( /W) D-Cache Clean by MVA to PoU */ + __OM uint32_t DCCMVAC; /*!< Offset: 0x268 ( /W) D-Cache Clean by MVA to PoC */ + __OM uint32_t DCCSW; /*!< Offset: 0x26C ( /W) D-Cache Clean by Set-way */ + __OM uint32_t DCCIMVAC; /*!< Offset: 0x270 ( /W) D-Cache Clean and Invalidate by MVA to PoC */ + __OM uint32_t DCCISW; /*!< Offset: 0x274 ( /W) D-Cache Clean and Invalidate by Set-way */ + uint32_t RESERVED7[6U]; + __IOM uint32_t ITCMCR; /*!< Offset: 0x290 (R/W) Instruction Tightly-Coupled Memory Control Register */ + __IOM uint32_t DTCMCR; /*!< Offset: 0x294 (R/W) Data Tightly-Coupled Memory Control Registers */ + __IOM uint32_t AHBPCR; /*!< Offset: 0x298 (R/W) AHBP Control Register */ + __IOM uint32_t CACR; /*!< Offset: 0x29C (R/W) L1 Cache Control Register */ + __IOM uint32_t AHBSCR; /*!< Offset: 0x2A0 (R/W) AHB Slave Control Register */ + uint32_t RESERVED8[1U]; + __IOM uint32_t ABFSR; /*!< Offset: 0x2A8 (R/W) Auxiliary Bus Fault Status Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_BP_Pos 18U /*!< SCB CCR: Branch prediction enable bit Position */ +#define SCB_CCR_BP_Msk (1UL << SCB_CCR_BP_Pos) /*!< SCB CCR: Branch prediction enable bit Mask */ + +#define SCB_CCR_IC_Pos 17U /*!< SCB CCR: Instruction cache enable bit Position */ +#define SCB_CCR_IC_Msk (1UL << SCB_CCR_IC_Pos) /*!< SCB CCR: Instruction cache enable bit Mask */ + +#define SCB_CCR_DC_Pos 16U /*!< SCB CCR: Cache enable bit Position */ +#define SCB_CCR_DC_Msk (1UL << SCB_CCR_DC_Pos) /*!< SCB CCR: Cache enable bit Mask */ + +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_SHCSR_MEMFAULTACT_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/* SCB Cache Level ID Register Definitions */ +#define SCB_CLIDR_LOUU_Pos 27U /*!< SCB CLIDR: LoUU Position */ +#define SCB_CLIDR_LOUU_Msk (7UL << SCB_CLIDR_LOUU_Pos) /*!< SCB CLIDR: LoUU Mask */ + +#define SCB_CLIDR_LOC_Pos 24U /*!< SCB CLIDR: LoC Position */ +#define SCB_CLIDR_LOC_Msk (7UL << SCB_CLIDR_LOC_Pos) /*!< SCB CLIDR: LoC Mask */ + +/* SCB Cache Type Register Definitions */ +#define SCB_CTR_FORMAT_Pos 29U /*!< SCB CTR: Format Position */ +#define SCB_CTR_FORMAT_Msk (7UL << SCB_CTR_FORMAT_Pos) /*!< SCB CTR: Format Mask */ + +#define SCB_CTR_CWG_Pos 24U /*!< SCB CTR: CWG Position */ +#define SCB_CTR_CWG_Msk (0xFUL << SCB_CTR_CWG_Pos) /*!< SCB CTR: CWG Mask */ + +#define SCB_CTR_ERG_Pos 20U /*!< SCB CTR: ERG Position */ +#define SCB_CTR_ERG_Msk (0xFUL << SCB_CTR_ERG_Pos) /*!< SCB CTR: ERG Mask */ + +#define SCB_CTR_DMINLINE_Pos 16U /*!< SCB CTR: DminLine Position */ +#define SCB_CTR_DMINLINE_Msk (0xFUL << SCB_CTR_DMINLINE_Pos) /*!< SCB CTR: DminLine Mask */ + +#define SCB_CTR_IMINLINE_Pos 0U /*!< SCB CTR: ImInLine Position */ +#define SCB_CTR_IMINLINE_Msk (0xFUL /*<< SCB_CTR_IMINLINE_Pos*/) /*!< SCB CTR: ImInLine Mask */ + +/* SCB Cache Size ID Register Definitions */ +#define SCB_CCSIDR_WT_Pos 31U /*!< SCB CCSIDR: WT Position */ +#define SCB_CCSIDR_WT_Msk (1UL << SCB_CCSIDR_WT_Pos) /*!< SCB CCSIDR: WT Mask */ + +#define SCB_CCSIDR_WB_Pos 30U /*!< SCB CCSIDR: WB Position */ +#define SCB_CCSIDR_WB_Msk (1UL << SCB_CCSIDR_WB_Pos) /*!< SCB CCSIDR: WB Mask */ + +#define SCB_CCSIDR_RA_Pos 29U /*!< SCB CCSIDR: RA Position */ +#define SCB_CCSIDR_RA_Msk (1UL << SCB_CCSIDR_RA_Pos) /*!< SCB CCSIDR: RA Mask */ + +#define SCB_CCSIDR_WA_Pos 28U /*!< SCB CCSIDR: WA Position */ +#define SCB_CCSIDR_WA_Msk (1UL << SCB_CCSIDR_WA_Pos) /*!< SCB CCSIDR: WA Mask */ + +#define SCB_CCSIDR_NUMSETS_Pos 13U /*!< SCB CCSIDR: NumSets Position */ +#define SCB_CCSIDR_NUMSETS_Msk (0x7FFFUL << SCB_CCSIDR_NUMSETS_Pos) /*!< SCB CCSIDR: NumSets Mask */ + +#define SCB_CCSIDR_ASSOCIATIVITY_Pos 3U /*!< SCB CCSIDR: Associativity Position */ +#define SCB_CCSIDR_ASSOCIATIVITY_Msk (0x3FFUL << SCB_CCSIDR_ASSOCIATIVITY_Pos) /*!< SCB CCSIDR: Associativity Mask */ + +#define SCB_CCSIDR_LINESIZE_Pos 0U /*!< SCB CCSIDR: LineSize Position */ +#define SCB_CCSIDR_LINESIZE_Msk (7UL /*<< SCB_CCSIDR_LINESIZE_Pos*/) /*!< SCB CCSIDR: LineSize Mask */ + +/* SCB Cache Size Selection Register Definitions */ +#define SCB_CSSELR_LEVEL_Pos 1U /*!< SCB CSSELR: Level Position */ +#define SCB_CSSELR_LEVEL_Msk (7UL << SCB_CSSELR_LEVEL_Pos) /*!< SCB CSSELR: Level Mask */ + +#define SCB_CSSELR_IND_Pos 0U /*!< SCB CSSELR: InD Position */ +#define SCB_CSSELR_IND_Msk (1UL /*<< SCB_CSSELR_IND_Pos*/) /*!< SCB CSSELR: InD Mask */ + +/* SCB Software Triggered Interrupt Register Definitions */ +#define SCB_STIR_INTID_Pos 0U /*!< SCB STIR: INTID Position */ +#define SCB_STIR_INTID_Msk (0x1FFUL /*<< SCB_STIR_INTID_Pos*/) /*!< SCB STIR: INTID Mask */ + +/* SCB D-Cache Invalidate by Set-way Register Definitions */ +#define SCB_DCISW_WAY_Pos 30U /*!< SCB DCISW: Way Position */ +#define SCB_DCISW_WAY_Msk (3UL << SCB_DCISW_WAY_Pos) /*!< SCB DCISW: Way Mask */ + +#define SCB_DCISW_SET_Pos 5U /*!< SCB DCISW: Set Position */ +#define SCB_DCISW_SET_Msk (0x1FFUL << SCB_DCISW_SET_Pos) /*!< SCB DCISW: Set Mask */ + +/* SCB D-Cache Clean by Set-way Register Definitions */ +#define SCB_DCCSW_WAY_Pos 30U /*!< SCB DCCSW: Way Position */ +#define SCB_DCCSW_WAY_Msk (3UL << SCB_DCCSW_WAY_Pos) /*!< SCB DCCSW: Way Mask */ + +#define SCB_DCCSW_SET_Pos 5U /*!< SCB DCCSW: Set Position */ +#define SCB_DCCSW_SET_Msk (0x1FFUL << SCB_DCCSW_SET_Pos) /*!< SCB DCCSW: Set Mask */ + +/* SCB D-Cache Clean and Invalidate by Set-way Register Definitions */ +#define SCB_DCCISW_WAY_Pos 30U /*!< SCB DCCISW: Way Position */ +#define SCB_DCCISW_WAY_Msk (3UL << SCB_DCCISW_WAY_Pos) /*!< SCB DCCISW: Way Mask */ + +#define SCB_DCCISW_SET_Pos 5U /*!< SCB DCCISW: Set Position */ +#define SCB_DCCISW_SET_Msk (0x1FFUL << SCB_DCCISW_SET_Pos) /*!< SCB DCCISW: Set Mask */ + +/* Instruction Tightly-Coupled Memory Control Register Definitions */ +#define SCB_ITCMCR_SZ_Pos 3U /*!< SCB ITCMCR: SZ Position */ +#define SCB_ITCMCR_SZ_Msk (0xFUL << SCB_ITCMCR_SZ_Pos) /*!< SCB ITCMCR: SZ Mask */ + +#define SCB_ITCMCR_RETEN_Pos 2U /*!< SCB ITCMCR: RETEN Position */ +#define SCB_ITCMCR_RETEN_Msk (1UL << SCB_ITCMCR_RETEN_Pos) /*!< SCB ITCMCR: RETEN Mask */ + +#define SCB_ITCMCR_RMW_Pos 1U /*!< SCB ITCMCR: RMW Position */ +#define SCB_ITCMCR_RMW_Msk (1UL << SCB_ITCMCR_RMW_Pos) /*!< SCB ITCMCR: RMW Mask */ + +#define SCB_ITCMCR_EN_Pos 0U /*!< SCB ITCMCR: EN Position */ +#define SCB_ITCMCR_EN_Msk (1UL /*<< SCB_ITCMCR_EN_Pos*/) /*!< SCB ITCMCR: EN Mask */ + +/* Data Tightly-Coupled Memory Control Register Definitions */ +#define SCB_DTCMCR_SZ_Pos 3U /*!< SCB DTCMCR: SZ Position */ +#define SCB_DTCMCR_SZ_Msk (0xFUL << SCB_DTCMCR_SZ_Pos) /*!< SCB DTCMCR: SZ Mask */ + +#define SCB_DTCMCR_RETEN_Pos 2U /*!< SCB DTCMCR: RETEN Position */ +#define SCB_DTCMCR_RETEN_Msk (1UL << SCB_DTCMCR_RETEN_Pos) /*!< SCB DTCMCR: RETEN Mask */ + +#define SCB_DTCMCR_RMW_Pos 1U /*!< SCB DTCMCR: RMW Position */ +#define SCB_DTCMCR_RMW_Msk (1UL << SCB_DTCMCR_RMW_Pos) /*!< SCB DTCMCR: RMW Mask */ + +#define SCB_DTCMCR_EN_Pos 0U /*!< SCB DTCMCR: EN Position */ +#define SCB_DTCMCR_EN_Msk (1UL /*<< SCB_DTCMCR_EN_Pos*/) /*!< SCB DTCMCR: EN Mask */ + +/* AHBP Control Register Definitions */ +#define SCB_AHBPCR_SZ_Pos 1U /*!< SCB AHBPCR: SZ Position */ +#define SCB_AHBPCR_SZ_Msk (7UL << SCB_AHBPCR_SZ_Pos) /*!< SCB AHBPCR: SZ Mask */ + +#define SCB_AHBPCR_EN_Pos 0U /*!< SCB AHBPCR: EN Position */ +#define SCB_AHBPCR_EN_Msk (1UL /*<< SCB_AHBPCR_EN_Pos*/) /*!< SCB AHBPCR: EN Mask */ + +/* L1 Cache Control Register Definitions */ +#define SCB_CACR_FORCEWT_Pos 2U /*!< SCB CACR: FORCEWT Position */ +#define SCB_CACR_FORCEWT_Msk (1UL << SCB_CACR_FORCEWT_Pos) /*!< SCB CACR: FORCEWT Mask */ + +#define SCB_CACR_ECCEN_Pos 1U /*!< SCB CACR: ECCEN Position */ +#define SCB_CACR_ECCEN_Msk (1UL << SCB_CACR_ECCEN_Pos) /*!< SCB CACR: ECCEN Mask */ + +#define SCB_CACR_SIWT_Pos 0U /*!< SCB CACR: SIWT Position */ +#define SCB_CACR_SIWT_Msk (1UL /*<< SCB_CACR_SIWT_Pos*/) /*!< SCB CACR: SIWT Mask */ + +/* AHBS Control Register Definitions */ +#define SCB_AHBSCR_INITCOUNT_Pos 11U /*!< SCB AHBSCR: INITCOUNT Position */ +#define SCB_AHBSCR_INITCOUNT_Msk (0x1FUL << SCB_AHBPCR_INITCOUNT_Pos) /*!< SCB AHBSCR: INITCOUNT Mask */ + +#define SCB_AHBSCR_TPRI_Pos 2U /*!< SCB AHBSCR: TPRI Position */ +#define SCB_AHBSCR_TPRI_Msk (0x1FFUL << SCB_AHBPCR_TPRI_Pos) /*!< SCB AHBSCR: TPRI Mask */ + +#define SCB_AHBSCR_CTL_Pos 0U /*!< SCB AHBSCR: CTL Position*/ +#define SCB_AHBSCR_CTL_Msk (3UL /*<< SCB_AHBPCR_CTL_Pos*/) /*!< SCB AHBSCR: CTL Mask */ + +/* Auxiliary Bus Fault Status Register Definitions */ +#define SCB_ABFSR_AXIMTYPE_Pos 8U /*!< SCB ABFSR: AXIMTYPE Position*/ +#define SCB_ABFSR_AXIMTYPE_Msk (3UL << SCB_ABFSR_AXIMTYPE_Pos) /*!< SCB ABFSR: AXIMTYPE Mask */ + +#define SCB_ABFSR_EPPB_Pos 4U /*!< SCB ABFSR: EPPB Position*/ +#define SCB_ABFSR_EPPB_Msk (1UL << SCB_ABFSR_EPPB_Pos) /*!< SCB ABFSR: EPPB Mask */ + +#define SCB_ABFSR_AXIM_Pos 3U /*!< SCB ABFSR: AXIM Position*/ +#define SCB_ABFSR_AXIM_Msk (1UL << SCB_ABFSR_AXIM_Pos) /*!< SCB ABFSR: AXIM Mask */ + +#define SCB_ABFSR_AHBP_Pos 2U /*!< SCB ABFSR: AHBP Position*/ +#define SCB_ABFSR_AHBP_Msk (1UL << SCB_ABFSR_AHBP_Pos) /*!< SCB ABFSR: AHBP Mask */ + +#define SCB_ABFSR_DTCM_Pos 1U /*!< SCB ABFSR: DTCM Position*/ +#define SCB_ABFSR_DTCM_Msk (1UL << SCB_ABFSR_DTCM_Pos) /*!< SCB ABFSR: DTCM Mask */ + +#define SCB_ABFSR_ITCM_Pos 0U /*!< SCB ABFSR: ITCM Position*/ +#define SCB_ABFSR_ITCM_Msk (1UL /*<< SCB_ABFSR_ITCM_Pos*/) /*!< SCB ABFSR: ITCM Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISDYNADD_Pos 26U /*!< ACTLR: DISDYNADD Position */ +#define SCnSCB_ACTLR_DISDYNADD_Msk (1UL << SCnSCB_ACTLR_DISDYNADD_Pos) /*!< ACTLR: DISDYNADD Mask */ + +#define SCnSCB_ACTLR_DISISSCH1_Pos 21U /*!< ACTLR: DISISSCH1 Position */ +#define SCnSCB_ACTLR_DISISSCH1_Msk (0x1FUL << SCnSCB_ACTLR_DISISSCH1_Pos) /*!< ACTLR: DISISSCH1 Mask */ + +#define SCnSCB_ACTLR_DISDI_Pos 16U /*!< ACTLR: DISDI Position */ +#define SCnSCB_ACTLR_DISDI_Msk (0x1FUL << SCnSCB_ACTLR_DISDI_Pos) /*!< ACTLR: DISDI Mask */ + +#define SCnSCB_ACTLR_DISCRITAXIRUR_Pos 15U /*!< ACTLR: DISCRITAXIRUR Position */ +#define SCnSCB_ACTLR_DISCRITAXIRUR_Msk (1UL << SCnSCB_ACTLR_DISCRITAXIRUR_Pos) /*!< ACTLR: DISCRITAXIRUR Mask */ + +#define SCnSCB_ACTLR_DISBTACALLOC_Pos 14U /*!< ACTLR: DISBTACALLOC Position */ +#define SCnSCB_ACTLR_DISBTACALLOC_Msk (1UL << SCnSCB_ACTLR_DISBTACALLOC_Pos) /*!< ACTLR: DISBTACALLOC Mask */ + +#define SCnSCB_ACTLR_DISBTACREAD_Pos 13U /*!< ACTLR: DISBTACREAD Position */ +#define SCnSCB_ACTLR_DISBTACREAD_Msk (1UL << SCnSCB_ACTLR_DISBTACREAD_Pos) /*!< ACTLR: DISBTACREAD Mask */ + +#define SCnSCB_ACTLR_DISITMATBFLUSH_Pos 12U /*!< ACTLR: DISITMATBFLUSH Position */ +#define SCnSCB_ACTLR_DISITMATBFLUSH_Msk (1UL << SCnSCB_ACTLR_DISITMATBFLUSH_Pos) /*!< ACTLR: DISITMATBFLUSH Mask */ + +#define SCnSCB_ACTLR_DISRAMODE_Pos 11U /*!< ACTLR: DISRAMODE Position */ +#define SCnSCB_ACTLR_DISRAMODE_Msk (1UL << SCnSCB_ACTLR_DISRAMODE_Pos) /*!< ACTLR: DISRAMODE Mask */ + +#define SCnSCB_ACTLR_FPEXCODIS_Pos 10U /*!< ACTLR: FPEXCODIS Position */ +#define SCnSCB_ACTLR_FPEXCODIS_Msk (1UL << SCnSCB_ACTLR_FPEXCODIS_Pos) /*!< ACTLR: FPEXCODIS Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[32U]; + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL /*<< ITM_LSR_Present_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ + uint32_t RESERVED3[981U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( W) Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R ) Lock Status Register */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x018 (R/ ) Media and FP Feature Register 2 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/* Media and FP Feature Register 2 Definitions */ + +#define FPU_MVFR2_VFP_Misc_Pos 4U /*!< MVFR2: VFP Misc bits Position */ +#define FPU_MVFR2_VFP_Misc_Msk (0xFUL << FPU_MVFR2_VFP_Misc_Pos) /*!< MVFR2: VFP Misc bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + __COMPILER_BARRIER(); + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __COMPILER_BARRIER(); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHPR[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + (* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)) = vector; + __DSB(); +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t vectors = (uint32_t )SCB->VTOR; + return (uint32_t)(* (int *) (vectors + ((int32_t)IRQn + NVIC_USER_IRQ_OFFSET) * 4)); +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = SCB->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x220U) + { + return 2U; /* Double + Single precision FPU */ + } + else if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ########################## Cache functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_CacheFunctions Cache Functions + \brief Functions that configure Instruction and Data cache. + @{ + */ + +/* Cache Size ID Register Macros */ +#define CCSIDR_WAYS(x) (((x) & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos) +#define CCSIDR_SETS(x) (((x) & SCB_CCSIDR_NUMSETS_Msk ) >> SCB_CCSIDR_NUMSETS_Pos ) + +#define __SCB_DCACHE_LINE_SIZE 32U /*!< Cortex-M7 cache line size is fixed to 32 bytes (8 words). See also register SCB_CCSIDR */ +#define __SCB_ICACHE_LINE_SIZE 32U /*!< Cortex-M7 cache line size is fixed to 32 bytes (8 words). See also register SCB_CCSIDR */ + +/** + \brief Enable I-Cache + \details Turns on I-Cache + */ +__STATIC_FORCEINLINE void SCB_EnableICache (void) +{ + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) + if (SCB->CCR & SCB_CCR_IC_Msk) return; /* return if ICache is already enabled */ + + __DSB(); + __ISB(); + SCB->ICIALLU = 0UL; /* invalidate I-Cache */ + __DSB(); + __ISB(); + SCB->CCR |= (uint32_t)SCB_CCR_IC_Msk; /* enable I-Cache */ + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Disable I-Cache + \details Turns off I-Cache + */ +__STATIC_FORCEINLINE void SCB_DisableICache (void) +{ + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) + __DSB(); + __ISB(); + SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk; /* disable I-Cache */ + SCB->ICIALLU = 0UL; /* invalidate I-Cache */ + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Invalidate I-Cache + \details Invalidates I-Cache + */ +__STATIC_FORCEINLINE void SCB_InvalidateICache (void) +{ + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) + __DSB(); + __ISB(); + SCB->ICIALLU = 0UL; + __DSB(); + __ISB(); + #endif +} + + +/** + \brief I-Cache Invalidate by address + \details Invalidates I-Cache for the given address. + I-Cache is invalidated starting from a 32 byte aligned address in 32 byte granularity. + I-Cache memory blocks which are part of given address + given size are invalidated. + \param[in] addr address + \param[in] isize size of memory block (in number of bytes) +*/ +__STATIC_FORCEINLINE void SCB_InvalidateICache_by_Addr (void *addr, int32_t isize) +{ + #if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) + if ( isize > 0 ) { + int32_t op_size = isize + (((uint32_t)addr) & (__SCB_ICACHE_LINE_SIZE - 1U)); + uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_ICACHE_LINE_SIZE - 1U) */; + + __DSB(); + + do { + SCB->ICIMVAU = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */ + op_addr += __SCB_ICACHE_LINE_SIZE; + op_size -= __SCB_ICACHE_LINE_SIZE; + } while ( op_size > 0 ); + + __DSB(); + __ISB(); + } + #endif +} + + +/** + \brief Enable D-Cache + \details Turns on D-Cache + */ +__STATIC_FORCEINLINE void SCB_EnableDCache (void) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + uint32_t ccsidr; + uint32_t sets; + uint32_t ways; + + if (SCB->CCR & SCB_CCR_DC_Msk) return; /* return if DCache is already enabled */ + + SCB->CSSELR = 0U; /* select Level 1 data cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; + + /* invalidate D-Cache */ + sets = (uint32_t)(CCSIDR_SETS(ccsidr)); + do { + ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); + do { + SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) | + ((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) ); + #if defined ( __CC_ARM ) + __schedule_barrier(); + #endif + } while (ways-- != 0U); + } while(sets-- != 0U); + __DSB(); + + SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */ + + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Disable D-Cache + \details Turns off D-Cache + */ +__STATIC_FORCEINLINE void SCB_DisableDCache (void) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + uint32_t ccsidr; + uint32_t sets; + uint32_t ways; + + SCB->CSSELR = 0U; /* select Level 1 data cache */ + __DSB(); + + SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; + + /* clean & invalidate D-Cache */ + sets = (uint32_t)(CCSIDR_SETS(ccsidr)); + do { + ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); + do { + SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | + ((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) ); + #if defined ( __CC_ARM ) + __schedule_barrier(); + #endif + } while (ways-- != 0U); + } while(sets-- != 0U); + + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Invalidate D-Cache + \details Invalidates D-Cache + */ +__STATIC_FORCEINLINE void SCB_InvalidateDCache (void) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + uint32_t ccsidr; + uint32_t sets; + uint32_t ways; + + SCB->CSSELR = 0U; /* select Level 1 data cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; + + /* invalidate D-Cache */ + sets = (uint32_t)(CCSIDR_SETS(ccsidr)); + do { + ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); + do { + SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) | + ((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) ); + #if defined ( __CC_ARM ) + __schedule_barrier(); + #endif + } while (ways-- != 0U); + } while(sets-- != 0U); + + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Clean D-Cache + \details Cleans D-Cache + */ +__STATIC_FORCEINLINE void SCB_CleanDCache (void) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + uint32_t ccsidr; + uint32_t sets; + uint32_t ways; + + SCB->CSSELR = 0U; /* select Level 1 data cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; + + /* clean D-Cache */ + sets = (uint32_t)(CCSIDR_SETS(ccsidr)); + do { + ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); + do { + SCB->DCCSW = (((sets << SCB_DCCSW_SET_Pos) & SCB_DCCSW_SET_Msk) | + ((ways << SCB_DCCSW_WAY_Pos) & SCB_DCCSW_WAY_Msk) ); + #if defined ( __CC_ARM ) + __schedule_barrier(); + #endif + } while (ways-- != 0U); + } while(sets-- != 0U); + + __DSB(); + __ISB(); + #endif +} + + +/** + \brief Clean & Invalidate D-Cache + \details Cleans and Invalidates D-Cache + */ +__STATIC_FORCEINLINE void SCB_CleanInvalidateDCache (void) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + uint32_t ccsidr; + uint32_t sets; + uint32_t ways; + + SCB->CSSELR = 0U; /* select Level 1 data cache */ + __DSB(); + + ccsidr = SCB->CCSIDR; + + /* clean & invalidate D-Cache */ + sets = (uint32_t)(CCSIDR_SETS(ccsidr)); + do { + ways = (uint32_t)(CCSIDR_WAYS(ccsidr)); + do { + SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) | + ((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) ); + #if defined ( __CC_ARM ) + __schedule_barrier(); + #endif + } while (ways-- != 0U); + } while(sets-- != 0U); + + __DSB(); + __ISB(); + #endif +} + + +/** + \brief D-Cache Invalidate by address + \details Invalidates D-Cache for the given address. + D-Cache is invalidated starting from a 32 byte aligned address in 32 byte granularity. + D-Cache memory blocks which are part of given address + given size are invalidated. + \param[in] addr address + \param[in] dsize size of memory block (in number of bytes) +*/ +__STATIC_FORCEINLINE void SCB_InvalidateDCache_by_Addr (void *addr, int32_t dsize) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + if ( dsize > 0 ) { + int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U)); + uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */; + + __DSB(); + + do { + SCB->DCIMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */ + op_addr += __SCB_DCACHE_LINE_SIZE; + op_size -= __SCB_DCACHE_LINE_SIZE; + } while ( op_size > 0 ); + + __DSB(); + __ISB(); + } + #endif +} + + +/** + \brief D-Cache Clean by address + \details Cleans D-Cache for the given address + D-Cache is cleaned starting from a 32 byte aligned address in 32 byte granularity. + D-Cache memory blocks which are part of given address + given size are cleaned. + \param[in] addr address + \param[in] dsize size of memory block (in number of bytes) +*/ +__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr (uint32_t *addr, int32_t dsize) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + if ( dsize > 0 ) { + int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U)); + uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */; + + __DSB(); + + do { + SCB->DCCMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */ + op_addr += __SCB_DCACHE_LINE_SIZE; + op_size -= __SCB_DCACHE_LINE_SIZE; + } while ( op_size > 0 ); + + __DSB(); + __ISB(); + } + #endif +} + + +/** + \brief D-Cache Clean and Invalidate by address + \details Cleans and invalidates D_Cache for the given address + D-Cache is cleaned and invalidated starting from a 32 byte aligned address in 32 byte granularity. + D-Cache memory blocks which are part of given address + given size are cleaned and invalidated. + \param[in] addr address (aligned to 32-byte boundary) + \param[in] dsize size of memory block (in number of bytes) +*/ +__STATIC_FORCEINLINE void SCB_CleanInvalidateDCache_by_Addr (uint32_t *addr, int32_t dsize) +{ + #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) + if ( dsize > 0 ) { + int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U)); + uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */; + + __DSB(); + + do { + SCB->DCCIMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */ + op_addr += __SCB_DCACHE_LINE_SIZE; + op_size -= __SCB_DCACHE_LINE_SIZE; + } while ( op_size > 0 ); + + __DSB(); + __ISB(); + } + #endif +} + +/*@} end of CMSIS_Core_CacheFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM7_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/panda/board/stm32h7/inc/mpu_armv8.h b/panda/board/stm32h7/inc/mpu_armv8.h new file mode 100644 index 0000000..2fe28b6 --- /dev/null +++ b/panda/board/stm32h7/inc/mpu_armv8.h @@ -0,0 +1,346 @@ +/****************************************************************************** + * @file mpu_armv8.h + * @brief CMSIS MPU API for Armv8-M and Armv8.1-M MPU + * @version V5.1.0 + * @date 08. March 2019 + ******************************************************************************/ +/* + * Copyright (c) 2017-2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV8_H +#define ARM_MPU_ARMV8_H + +/** \brief Attribute for device memory (outer only) */ +#define ARM_MPU_ATTR_DEVICE ( 0U ) + +/** \brief Attribute for non-cacheable, normal memory */ +#define ARM_MPU_ATTR_NON_CACHEABLE ( 4U ) + +/** \brief Attribute for normal memory (outer and inner) +* \param NT Non-Transient: Set to 1 for non-transient data. +* \param WB Write-Back: Set to 1 to use write-back update policy. +* \param RA Read Allocation: Set to 1 to use cache allocation on read miss. +* \param WA Write Allocation: Set to 1 to use cache allocation on write miss. +*/ +#define ARM_MPU_ATTR_MEMORY_(NT, WB, RA, WA) \ + (((NT & 1U) << 3U) | ((WB & 1U) << 2U) | ((RA & 1U) << 1U) | (WA & 1U)) + +/** \brief Device memory type non Gathering, non Re-ordering, non Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRnE (0U) + +/** \brief Device memory type non Gathering, non Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGnRE (1U) + +/** \brief Device memory type non Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_nGRE (2U) + +/** \brief Device memory type Gathering, Re-ordering, Early Write Acknowledgement */ +#define ARM_MPU_ATTR_DEVICE_GRE (3U) + +/** \brief Memory Attribute +* \param O Outer memory attributes +* \param I O == ARM_MPU_ATTR_DEVICE: Device memory attributes, else: Inner memory attributes +*/ +#define ARM_MPU_ATTR(O, I) (((O & 0xFU) << 4U) | (((O & 0xFU) != 0U) ? (I & 0xFU) : ((I & 0x3U) << 2U))) + +/** \brief Normal memory non-shareable */ +#define ARM_MPU_SH_NON (0U) + +/** \brief Normal memory outer shareable */ +#define ARM_MPU_SH_OUTER (2U) + +/** \brief Normal memory inner shareable */ +#define ARM_MPU_SH_INNER (3U) + +/** \brief Memory access permissions +* \param RO Read-Only: Set to 1 for read-only memory. +* \param NP Non-Privileged: Set to 1 for non-privileged memory. +*/ +#define ARM_MPU_AP_(RO, NP) (((RO & 1U) << 1U) | (NP & 1U)) + +/** \brief Region Base Address Register value +* \param BASE The base address bits [31:5] of a memory region. The value is zero extended. Effective address gets 32 byte aligned. +* \param SH Defines the Shareability domain for this memory region. +* \param RO Read-Only: Set to 1 for a read-only memory region. +* \param NP Non-Privileged: Set to 1 for a non-privileged memory region. +* \oaram XN eXecute Never: Set to 1 for a non-executable memory region. +*/ +#define ARM_MPU_RBAR(BASE, SH, RO, NP, XN) \ + ((BASE & MPU_RBAR_BASE_Msk) | \ + ((SH << MPU_RBAR_SH_Pos) & MPU_RBAR_SH_Msk) | \ + ((ARM_MPU_AP_(RO, NP) << MPU_RBAR_AP_Pos) & MPU_RBAR_AP_Msk) | \ + ((XN << MPU_RBAR_XN_Pos) & MPU_RBAR_XN_Msk)) + +/** \brief Region Limit Address Register value +* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended. +* \param IDX The attribute index to be associated with this memory region. +*/ +#define ARM_MPU_RLAR(LIMIT, IDX) \ + ((LIMIT & MPU_RLAR_LIMIT_Msk) | \ + ((IDX << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \ + (MPU_RLAR_EN_Msk)) + +#if defined(MPU_RLAR_PXN_Pos) + +/** \brief Region Limit Address Register with PXN value +* \param LIMIT The limit address bits [31:5] for this memory region. The value is one extended. +* \param PXN Privileged execute never. Defines whether code can be executed from this privileged region. +* \param IDX The attribute index to be associated with this memory region. +*/ +#define ARM_MPU_RLAR_PXN(LIMIT, PXN, IDX) \ + ((LIMIT & MPU_RLAR_LIMIT_Msk) | \ + ((PXN << MPU_RLAR_PXN_Pos) & MPU_RLAR_PXN_Msk) | \ + ((IDX << MPU_RLAR_AttrIndx_Pos) & MPU_RLAR_AttrIndx_Msk) | \ + (MPU_RLAR_EN_Msk)) + +#endif + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; /*!< Region Base Address Register value */ + uint32_t RLAR; /*!< Region Limit Address Register value */ +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif + __DSB(); + __ISB(); +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DMB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} + +#ifdef MPU_NS +/** Enable the Non-secure MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable_NS(uint32_t MPU_Control) +{ + MPU_NS->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif + __DSB(); + __ISB(); +} + +/** Disable the Non-secure MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable_NS(void) +{ + __DMB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB_NS->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU_NS->CTRL &= ~MPU_CTRL_ENABLE_Msk; +} +#endif + +/** Set the memory attribute encoding to the given MPU. +* \param mpu Pointer to the MPU to be configured. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttrEx(MPU_Type* mpu, uint8_t idx, uint8_t attr) +{ + const uint8_t reg = idx / 4U; + const uint32_t pos = ((idx % 4U) * 8U); + const uint32_t mask = 0xFFU << pos; + + if (reg >= (sizeof(mpu->MAIR) / sizeof(mpu->MAIR[0]))) { + return; // invalid index + } + + mpu->MAIR[reg] = ((mpu->MAIR[reg] & ~mask) | ((attr << pos) & mask)); +} + +/** Set the memory attribute encoding. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU, idx, attr); +} + +#ifdef MPU_NS +/** Set the memory attribute encoding to the Non-secure MPU. +* \param idx The attribute index to be set [0-7] +* \param attr The attribute value to be set. +*/ +__STATIC_INLINE void ARM_MPU_SetMemAttr_NS(uint8_t idx, uint8_t attr) +{ + ARM_MPU_SetMemAttrEx(MPU_NS, idx, attr); +} +#endif + +/** Clear and disable the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegionEx(MPU_Type* mpu, uint32_t rnr) +{ + mpu->RNR = rnr; + mpu->RLAR = 0U; +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU, rnr); +} + +#ifdef MPU_NS +/** Clear and disable the given Non-secure MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion_NS(uint32_t rnr) +{ + ARM_MPU_ClrRegionEx(MPU_NS, rnr); +} +#endif + +/** Configure the given MPU region of the given MPU. +* \param mpu Pointer to MPU to be used. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(MPU_Type* mpu, uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + mpu->RNR = rnr; + mpu->RBAR = rbar; + mpu->RLAR = rlar; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU, rnr, rbar, rlar); +} + +#ifdef MPU_NS +/** Configure the given Non-secure MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rlar Value for RLAR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion_NS(uint32_t rnr, uint32_t rbar, uint32_t rlar) +{ + ARM_MPU_SetRegionEx(MPU_NS, rnr, rbar, rlar); +} +#endif + +/** Memcopy with strictly ordered memory access, e.g. for register targets. +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table to the given MPU. +* \param mpu Pointer to the MPU registers to be used. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_LoadEx(MPU_Type* mpu, uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + if (cnt == 1U) { + mpu->RNR = rnr; + ARM_MPU_OrderedMemcpy(&(mpu->RBAR), &(table->RBAR), rowWordSize); + } else { + uint32_t rnrBase = rnr & ~(MPU_TYPE_RALIASES-1U); + uint32_t rnrOffset = rnr % MPU_TYPE_RALIASES; + + mpu->RNR = rnrBase; + while ((rnrOffset + cnt) > MPU_TYPE_RALIASES) { + uint32_t c = MPU_TYPE_RALIASES - rnrOffset; + ARM_MPU_OrderedMemcpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), c*rowWordSize); + table += c; + cnt -= c; + rnrOffset = 0U; + rnrBase += MPU_TYPE_RALIASES; + mpu->RNR = rnrBase; + } + + ARM_MPU_OrderedMemcpy(&(mpu->RBAR)+(rnrOffset*2U), &(table->RBAR), cnt*rowWordSize); + } +} + +/** Load the given number of MPU regions from a table. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU, rnr, table, cnt); +} + +#ifdef MPU_NS +/** Load the given number of MPU regions from a table to the Non-secure MPU. +* \param rnr First region number to be configured. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load_NS(uint32_t rnr, ARM_MPU_Region_t const* table, uint32_t cnt) +{ + ARM_MPU_LoadEx(MPU_NS, rnr, table, cnt); +} +#endif + +#endif + diff --git a/panda/board/stm32h7/inc/stm32h725xx.h b/panda/board/stm32h7/inc/stm32h725xx.h new file mode 100644 index 0000000..0e0d4a9 --- /dev/null +++ b/panda/board/stm32h7/inc/stm32h725xx.h @@ -0,0 +1,24740 @@ +/** + ****************************************************************************** + * @file stm32h735xx.h + * @author MCD Application Team + * @brief CMSIS STM32H735xx Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - Peripheral's registers declarations and bits definition + * - Macros to access peripheral's registers hardware + * + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32h735xx + * @{ + */ + +#ifndef STM32H735xx_H +#define STM32H735xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32H7XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M Processor Exceptions Numbers *****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 4 Cortex-M Memory Management Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt ( wwdg1_it, wwdg2_it) */ + PVD_AVD_IRQn = 1, /*!< PVD/AVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1 and ADC2 global Interrupts */ + FDCAN1_IT0_IRQn = 19, /*!< FDCAN1 Interrupt line 0 */ + FDCAN2_IT0_IRQn = 20, /*!< FDCAN2 Interrupt line 0 */ + FDCAN1_IT1_IRQn = 21, /*!< FDCAN1 Interrupt line 1 */ + FDCAN2_IT1_IRQn = 22, /*!< FDCAN2 Interrupt line 1 */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FMC_IRQn = 48, /*!< FMC global Interrupt */ + SDMMC1_IRQn = 49, /*!< SDMMC1 global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + ETH_IRQn = 61, /*!< Ethernet global Interrupt */ + ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */ + FDCAN_CAL_IRQn = 63, /*!< FDCAN Calibration unit Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */ + OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */ + OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */ + OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */ + DCMI_PSSI_IRQn = 78, /*!< DCMI and PSSI global interrupt */ + CRYP_IRQn = 79, /*!< CRYP crypto global interrupt */ + HASH_RNG_IRQn = 80, /*!< HASH and RNG global interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SPI6_IRQn = 86, /*!< SPI6 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + LTDC_IRQn = 88, /*!< LTDC global Interrupt */ + LTDC_ER_IRQn = 89, /*!< LTDC Error global Interrupt */ + DMA2D_IRQn = 90, /*!< DMA2D global Interrupt */ + OCTOSPI1_IRQn = 92, /*!< OCTOSPI1 global interrupt */ + LPTIM1_IRQn = 93, /*!< LP TIM1 interrupt */ + CEC_IRQn = 94, /*!< HDMI-CEC global Interrupt */ + I2C4_EV_IRQn = 95, /*!< I2C4 Event Interrupt */ + I2C4_ER_IRQn = 96, /*!< I2C4 Error Interrupt */ + SPDIF_RX_IRQn = 97, /*!< SPDIF-RX global Interrupt */ + DMAMUX1_OVR_IRQn = 102, /*! + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + __IO uint32_t CFGR2; /*!< ADC Configuration register 2, Address offset: 0x10 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + __IO uint32_t PCSEL_RES0; /*!< Rserved for ADC3, ADC1/2 pre-channel selection, Address offset: 0x1C */ + __IO uint32_t LTR1_TR1; /*!< ADC watchdog Lower threshold register 1, Address offset: 0x20 */ + __IO uint32_t HTR1_TR2; /*!< ADC watchdog higher threshold register 1, Address offset: 0x24 */ + __IO uint32_t RES1_TR3; /*!< Rserved for ADC1/2, ADC3 threshold register, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t LTR2_DIFSEL; /*!< ADC watchdog Lower threshold register 2, Difsel for ADC3, Address offset: 0xB0 */ + __IO uint32_t HTR2_CALFACT; /*!< ADC watchdog Higher threshold register 2, Calfact for ADC3, Address offset: 0xB4 */ + __IO uint32_t LTR3_RES10; /*!< ADC watchdog Lower threshold register 3, specific ADC1/2, Address offset: 0xB8 */ + __IO uint32_t HTR3_RES11; /*!< ADC watchdog Higher threshold register 3, specific ADC1/2, Address offset: 0xBC */ + __IO uint32_t DIFSEL_RES12; /*!< ADC Differential Mode Selection Register specific ADC1/2, Address offset: 0xC0 */ + __IO uint32_t CALFACT_RES13; /*!< ADC Calibration Factors specific ADC1/2, Address offset: 0xC4 */ + __IO uint32_t CALFACT2_RES14; /*!< ADC Linearity Calibration Factors specific ADC1/2, Address offset: 0xC8 */ +} ADC_TypeDef; + + +typedef struct +{ +__IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ +uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ +__IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ +__IO uint32_t CDR; /*!< ADC common regular data register for dual Address offset: ADC1/3 base address + 0x30C */ +__IO uint32_t CDR2; /*!< ADC common regular data register for 32-bit dual mode Address offset: ADC1/3 base address + 0x310 */ + +} ADC_Common_TypeDef; + + +/** + * @brief VREFBUF + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< VREFBUF control and status register, Address offset: 0x00 */ + __IO uint32_t CCR; /*!< VREFBUF calibration and control register, Address offset: 0x04 */ +} VREFBUF_TypeDef; + + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< FDCAN Core Release register, Address offset: 0x000 */ + __IO uint32_t ENDN; /*!< FDCAN Endian register, Address offset: 0x004 */ + __IO uint32_t RESERVED1; /*!< Reserved, 0x008 */ + __IO uint32_t DBTP; /*!< FDCAN Data Bit Timing & Prescaler register, Address offset: 0x00C */ + __IO uint32_t TEST; /*!< FDCAN Test register, Address offset: 0x010 */ + __IO uint32_t RWD; /*!< FDCAN RAM Watchdog register, Address offset: 0x014 */ + __IO uint32_t CCCR; /*!< FDCAN CC Control register, Address offset: 0x018 */ + __IO uint32_t NBTP; /*!< FDCAN Nominal Bit Timing & Prescaler register, Address offset: 0x01C */ + __IO uint32_t TSCC; /*!< FDCAN Timestamp Counter Configuration register, Address offset: 0x020 */ + __IO uint32_t TSCV; /*!< FDCAN Timestamp Counter Value register, Address offset: 0x024 */ + __IO uint32_t TOCC; /*!< FDCAN Timeout Counter Configuration register, Address offset: 0x028 */ + __IO uint32_t TOCV; /*!< FDCAN Timeout Counter Value register, Address offset: 0x02C */ + __IO uint32_t RESERVED2[4]; /*!< Reserved, 0x030 - 0x03C */ + __IO uint32_t ECR; /*!< FDCAN Error Counter register, Address offset: 0x040 */ + __IO uint32_t PSR; /*!< FDCAN Protocol Status register, Address offset: 0x044 */ + __IO uint32_t TDCR; /*!< FDCAN Transmitter Delay Compensation register, Address offset: 0x048 */ + __IO uint32_t RESERVED3; /*!< Reserved, 0x04C */ + __IO uint32_t IR; /*!< FDCAN Interrupt register, Address offset: 0x050 */ + __IO uint32_t IE; /*!< FDCAN Interrupt Enable register, Address offset: 0x054 */ + __IO uint32_t ILS; /*!< FDCAN Interrupt Line Select register, Address offset: 0x058 */ + __IO uint32_t ILE; /*!< FDCAN Interrupt Line Enable register, Address offset: 0x05C */ + __IO uint32_t RESERVED4[8]; /*!< Reserved, 0x060 - 0x07C */ + __IO uint32_t GFC; /*!< FDCAN Global Filter Configuration register, Address offset: 0x080 */ + __IO uint32_t SIDFC; /*!< FDCAN Standard ID Filter Configuration register, Address offset: 0x084 */ + __IO uint32_t XIDFC; /*!< FDCAN Extended ID Filter Configuration register, Address offset: 0x088 */ + __IO uint32_t RESERVED5; /*!< Reserved, 0x08C */ + __IO uint32_t XIDAM; /*!< FDCAN Extended ID AND Mask register, Address offset: 0x090 */ + __IO uint32_t HPMS; /*!< FDCAN High Priority Message Status register, Address offset: 0x094 */ + __IO uint32_t NDAT1; /*!< FDCAN New Data 1 register, Address offset: 0x098 */ + __IO uint32_t NDAT2; /*!< FDCAN New Data 2 register, Address offset: 0x09C */ + __IO uint32_t RXF0C; /*!< FDCAN Rx FIFO 0 Configuration register, Address offset: 0x0A0 */ + __IO uint32_t RXF0S; /*!< FDCAN Rx FIFO 0 Status register, Address offset: 0x0A4 */ + __IO uint32_t RXF0A; /*!< FDCAN Rx FIFO 0 Acknowledge register, Address offset: 0x0A8 */ + __IO uint32_t RXBC; /*!< FDCAN Rx Buffer Configuration register, Address offset: 0x0AC */ + __IO uint32_t RXF1C; /*!< FDCAN Rx FIFO 1 Configuration register, Address offset: 0x0B0 */ + __IO uint32_t RXF1S; /*!< FDCAN Rx FIFO 1 Status register, Address offset: 0x0B4 */ + __IO uint32_t RXF1A; /*!< FDCAN Rx FIFO 1 Acknowledge register, Address offset: 0x0B8 */ + __IO uint32_t RXESC; /*!< FDCAN Rx Buffer/FIFO Element Size Configuration register, Address offset: 0x0BC */ + __IO uint32_t TXBC; /*!< FDCAN Tx Buffer Configuration register, Address offset: 0x0C0 */ + __IO uint32_t TXFQS; /*!< FDCAN Tx FIFO/Queue Status register, Address offset: 0x0C4 */ + __IO uint32_t TXESC; /*!< FDCAN Tx Buffer Element Size Configuration register, Address offset: 0x0C8 */ + __IO uint32_t TXBRP; /*!< FDCAN Tx Buffer Request Pending register, Address offset: 0x0CC */ + __IO uint32_t TXBAR; /*!< FDCAN Tx Buffer Add Request register, Address offset: 0x0D0 */ + __IO uint32_t TXBCR; /*!< FDCAN Tx Buffer Cancellation Request register, Address offset: 0x0D4 */ + __IO uint32_t TXBTO; /*!< FDCAN Tx Buffer Transmission Occurred register, Address offset: 0x0D8 */ + __IO uint32_t TXBCF; /*!< FDCAN Tx Buffer Cancellation Finished register, Address offset: 0x0DC */ + __IO uint32_t TXBTIE; /*!< FDCAN Tx Buffer Transmission Interrupt Enable register, Address offset: 0x0E0 */ + __IO uint32_t TXBCIE; /*!< FDCAN Tx Buffer Cancellation Finished Interrupt Enable register, Address offset: 0x0E4 */ + __IO uint32_t RESERVED6[2]; /*!< Reserved, 0x0E8 - 0x0EC */ + __IO uint32_t TXEFC; /*!< FDCAN Tx Event FIFO Configuration register, Address offset: 0x0F0 */ + __IO uint32_t TXEFS; /*!< FDCAN Tx Event FIFO Status register, Address offset: 0x0F4 */ + __IO uint32_t TXEFA; /*!< FDCAN Tx Event FIFO Acknowledge register, Address offset: 0x0F8 */ + __IO uint32_t RESERVED7; /*!< Reserved, 0x0FC */ +} FDCAN_GlobalTypeDef; + +/** + * @brief TTFD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t TTTMC; /*!< TT Trigger Memory Configuration register, Address offset: 0x100 */ + __IO uint32_t TTRMC; /*!< TT Reference Message Configuration register, Address offset: 0x104 */ + __IO uint32_t TTOCF; /*!< TT Operation Configuration register, Address offset: 0x108 */ + __IO uint32_t TTMLM; /*!< TT Matrix Limits register, Address offset: 0x10C */ + __IO uint32_t TURCF; /*!< TUR Configuration register, Address offset: 0x110 */ + __IO uint32_t TTOCN; /*!< TT Operation Control register, Address offset: 0x114 */ + __IO uint32_t TTGTP; /*!< TT Global Time Preset register, Address offset: 0x118 */ + __IO uint32_t TTTMK; /*!< TT Time Mark register, Address offset: 0x11C */ + __IO uint32_t TTIR; /*!< TT Interrupt register, Address offset: 0x120 */ + __IO uint32_t TTIE; /*!< TT Interrupt Enable register, Address offset: 0x124 */ + __IO uint32_t TTILS; /*!< TT Interrupt Line Select register, Address offset: 0x128 */ + __IO uint32_t TTOST; /*!< TT Operation Status register, Address offset: 0x12C */ + __IO uint32_t TURNA; /*!< TT TUR Numerator Actual register, Address offset: 0x130 */ + __IO uint32_t TTLGT; /*!< TT Local and Global Time register, Address offset: 0x134 */ + __IO uint32_t TTCTC; /*!< TT Cycle Time and Count register, Address offset: 0x138 */ + __IO uint32_t TTCPT; /*!< TT Capture Time register, Address offset: 0x13C */ + __IO uint32_t TTCSM; /*!< TT Cycle Sync Mark register, Address offset: 0x140 */ + __IO uint32_t RESERVED1[111]; /*!< Reserved, 0x144 - 0x2FC */ + __IO uint32_t TTTS; /*!< TT Trigger Select register, Address offset: 0x300 */ +} TTCAN_TypeDef; + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< Clock Calibration Unit Core Release register, Address offset: 0x00 */ + __IO uint32_t CCFG; /*!< Calibration Configuration register, Address offset: 0x04 */ + __IO uint32_t CSTAT; /*!< Calibration Status register, Address offset: 0x08 */ + __IO uint32_t CWD; /*!< Calibration Watchdog register, Address offset: 0x0C */ + __IO uint32_t IR; /*!< CCU Interrupt register, Address offset: 0x10 */ + __IO uint32_t IE; /*!< CCU Interrupt Enable register, Address offset: 0x14 */ +} FDCAN_ClockCalibrationUnit_TypeDef; + + +/** + * @brief Consumer Electronics Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< CEC control register, Address offset:0x00 */ + __IO uint32_t CFGR; /*!< CEC configuration register, Address offset:0x04 */ + __IO uint32_t TXDR; /*!< CEC Tx data register , Address offset:0x08 */ + __IO uint32_t RXDR; /*!< CEC Rx Data Register, Address offset:0x0C */ + __IO uint32_t ISR; /*!< CEC Interrupt and Status Register, Address offset:0x10 */ + __IO uint32_t IER; /*!< CEC interrupt enable register, Address offset:0x14 */ +}CEC_TypeDef; + +/** + * @brief COordincate Rotation DIgital Computer + */ +typedef struct +{ + __IO uint32_t CSR; /*!< CORDIC control and status register, Address offset: 0x00 */ + __IO uint32_t WDATA; /*!< CORDIC argument register, Address offset: 0x04 */ + __IO uint32_t RDATA; /*!< CORDIC result register, Address offset: 0x08 */ +} CORDIC_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint32_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + + +/** + * @brief Clock Recovery System + */ +typedef struct +{ +__IO uint32_t CR; /*!< CRS ccontrol register, Address offset: 0x00 */ +__IO uint32_t CFGR; /*!< CRS configuration register, Address offset: 0x04 */ +__IO uint32_t ISR; /*!< CRS interrupt and status register, Address offset: 0x08 */ +__IO uint32_t ICR; /*!< CRS interrupt flag clear register, Address offset: 0x0C */ +} CRS_TypeDef; + + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ + __IO uint32_t CCR; /*!< DAC calibration control register, Address offset: 0x38 */ + __IO uint32_t MCR; /*!< DAC mode control register, Address offset: 0x3C */ + __IO uint32_t SHSR1; /*!< DAC Sample and Hold sample time register 1, Address offset: 0x40 */ + __IO uint32_t SHSR2; /*!< DAC Sample and Hold sample time register 2, Address offset: 0x44 */ + __IO uint32_t SHHR; /*!< DAC Sample and Hold hold time register, Address offset: 0x48 */ + __IO uint32_t SHRR; /*!< DAC Sample and Hold refresh time register, Address offset: 0x4C */ +} DAC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Debug MCU + */ +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + uint32_t RESERVED4[11]; /*!< Reserved, Address offset: 0x08 */ + __IO uint32_t APB3FZ1; /*!< Debug MCU APB3FZ1 freeze register, Address offset: 0x34 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x38 */ + __IO uint32_t APB1LFZ1; /*!< Debug MCU APB1LFZ1 freeze register, Address offset: 0x3C */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x40 */ + __IO uint32_t APB1HFZ1; /*!< Debug MCU APB1LFZ1 freeze register, Address offset: 0x44 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x48 */ + __IO uint32_t APB2FZ1; /*!< Debug MCU APB2FZ1 freeze register, Address offset: 0x4C */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0x50 */ + __IO uint32_t APB4FZ1; /*!< Debug MCU APB4FZ1 freeze register, Address offset: 0x54 */ + __IO uint32_t RESERVED9[990]; /*!< Reserved, Address offset: 0x58-0xFCC */ + __IO uint32_t PIDR4; /*!< Debug MCU peripheral identity register 4, Address offset: 0xFD0 */ + __IO uint32_t RESERVED10[3];/*!< Reserved, Address offset: 0xFD4-0xFDC */ + __IO uint32_t PIDR0; /*!< Debug MCU peripheral identity register 0, Address offset: 0xFE0 */ + __IO uint32_t PIDR1; /*!< Debug MCU peripheral identity register 1, Address offset: 0xFE4 */ + __IO uint32_t PIDR2; /*!< Debug MCU peripheral identity register 2, Address offset: 0xFE8 */ + __IO uint32_t PIDR3; /*!< Debug MCU peripheral identity register 3, Address offset: 0xFEC */ + __IO uint32_t CIDR0; /*!< Debug MCU component identity register 0, Address offset: 0xFF0 */ + __IO uint32_t CIDR1; /*!< Debug MCU component identity register 1, Address offset: 0xFF4 */ + __IO uint32_t CIDR2; /*!< Debug MCU component identity register 2, Address offset: 0xFF8 */ + __IO uint32_t CIDR3; /*!< Debug MCU component identity register 3, Address offset: 0xFFC */ +}DBGMCU_TypeDef; +/** + * @brief DCMI + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DCMI control register 1, Address offset: 0x00 */ + __IO uint32_t SR; /*!< DCMI status register, Address offset: 0x04 */ + __IO uint32_t RISR; /*!< DCMI raw interrupt status register, Address offset: 0x08 */ + __IO uint32_t IER; /*!< DCMI interrupt enable register, Address offset: 0x0C */ + __IO uint32_t MISR; /*!< DCMI masked interrupt status register, Address offset: 0x10 */ + __IO uint32_t ICR; /*!< DCMI interrupt clear register, Address offset: 0x14 */ + __IO uint32_t ESCR; /*!< DCMI embedded synchronization code register, Address offset: 0x18 */ + __IO uint32_t ESUR; /*!< DCMI embedded synchronization unmask register, Address offset: 0x1C */ + __IO uint32_t CWSTRTR; /*!< DCMI crop window start, Address offset: 0x20 */ + __IO uint32_t CWSIZER; /*!< DCMI crop window size, Address offset: 0x24 */ + __IO uint32_t DR; /*!< DCMI data register, Address offset: 0x28 */ +} DCMI_TypeDef; + +/** + * @brief PSSI + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PSSI control register 1, Address offset: 0x000 */ + __IO uint32_t SR; /*!< PSSI status register, Address offset: 0x004 */ + __IO uint32_t RIS; /*!< PSSI raw interrupt status register, Address offset: 0x008 */ + __IO uint32_t IER; /*!< PSSI interrupt enable register, Address offset: 0x00C */ + __IO uint32_t MIS; /*!< PSSI masked interrupt status register, Address offset: 0x010 */ + __IO uint32_t ICR; /*!< PSSI interrupt clear register, Address offset: 0x014 */ + __IO uint32_t RESERVED1[4]; /*!< Reserved, 0x018 - 0x024 */ + __IO uint32_t DR; /*!< PSSI data register, Address offset: 0x028 */ + __IO uint32_t RESERVED2[241]; /*!< Reserved, 0x02C - 0x3EC */ + __IO uint32_t HWCFGR; /*!< PSSI IP HW configuration register, Address offset: 0x3F0 */ + __IO uint32_t VERR; /*!< PSSI IP version register, Address offset: 0x3F4 */ + __IO uint32_t IPIDR; /*!< PSSI IP ID register, Address offset: 0x3F8 */ + __IO uint32_t SIDR; /*!< PSSI SIZE ID register, Address offset: 0x3FC */ +} PSSI_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CM0AR; /*!< DMA channel x memory 0 address register */ + __IO uint32_t CM1AR; /*!< DMA channel x memory 1 address register */ +} BDMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} BDMA_TypeDef; + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA Multiplexer Channel x Control Register */ +}DMAMUX_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< DMA Channel Status Register */ + __IO uint32_t CFR; /*!< DMA Channel Clear Flag Register */ +}DMAMUX_ChannelStatus_TypeDef; + +typedef struct +{ + __IO uint32_t RGCR; /*!< DMA Request Generator x Control Register */ +}DMAMUX_RequestGen_TypeDef; + +typedef struct +{ + __IO uint32_t RGSR; /*!< DMA Request Generator Status Register */ + __IO uint32_t RGCFR; /*!< DMA Request Generator Clear Flag Register */ +}DMAMUX_RequestGenStatus_TypeDef; + +/** + * @brief MDMA Controller + */ +typedef struct +{ + __IO uint32_t GISR0; /*!< MDMA Global Interrupt/Status Register 0, Address offset: 0x00 */ +}MDMA_TypeDef; + +typedef struct +{ + __IO uint32_t CISR; /*!< MDMA channel x interrupt/status register, Address offset: 0x40 */ + __IO uint32_t CIFCR; /*!< MDMA channel x interrupt flag clear register, Address offset: 0x44 */ + __IO uint32_t CESR; /*!< MDMA Channel x error status register, Address offset: 0x48 */ + __IO uint32_t CCR; /*!< MDMA channel x control register, Address offset: 0x4C */ + __IO uint32_t CTCR; /*!< MDMA channel x Transfer Configuration register, Address offset: 0x50 */ + __IO uint32_t CBNDTR; /*!< MDMA Channel x block number of data register, Address offset: 0x54 */ + __IO uint32_t CSAR; /*!< MDMA channel x source address register, Address offset: 0x58 */ + __IO uint32_t CDAR; /*!< MDMA channel x destination address register, Address offset: 0x5C */ + __IO uint32_t CBRUR; /*!< MDMA channel x Block Repeat address Update register, Address offset: 0x60 */ + __IO uint32_t CLAR; /*!< MDMA channel x Link Address register, Address offset: 0x64 */ + __IO uint32_t CTBR; /*!< MDMA channel x Trigger and Bus selection Register, Address offset: 0x68 */ + uint32_t RESERVED0; /*!< Reserved, 0x6C */ + __IO uint32_t CMAR; /*!< MDMA channel x Mask address register, Address offset: 0x70 */ + __IO uint32_t CMDR; /*!< MDMA channel x Mask Data register, Address offset: 0x74 */ +}MDMA_Channel_TypeDef; + +/** + * @brief DMA2D Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA2D Control Register, Address offset: 0x00 */ + __IO uint32_t ISR; /*!< DMA2D Interrupt Status Register, Address offset: 0x04 */ + __IO uint32_t IFCR; /*!< DMA2D Interrupt Flag Clear Register, Address offset: 0x08 */ + __IO uint32_t FGMAR; /*!< DMA2D Foreground Memory Address Register, Address offset: 0x0C */ + __IO uint32_t FGOR; /*!< DMA2D Foreground Offset Register, Address offset: 0x10 */ + __IO uint32_t BGMAR; /*!< DMA2D Background Memory Address Register, Address offset: 0x14 */ + __IO uint32_t BGOR; /*!< DMA2D Background Offset Register, Address offset: 0x18 */ + __IO uint32_t FGPFCCR; /*!< DMA2D Foreground PFC Control Register, Address offset: 0x1C */ + __IO uint32_t FGCOLR; /*!< DMA2D Foreground Color Register, Address offset: 0x20 */ + __IO uint32_t BGPFCCR; /*!< DMA2D Background PFC Control Register, Address offset: 0x24 */ + __IO uint32_t BGCOLR; /*!< DMA2D Background Color Register, Address offset: 0x28 */ + __IO uint32_t FGCMAR; /*!< DMA2D Foreground CLUT Memory Address Register, Address offset: 0x2C */ + __IO uint32_t BGCMAR; /*!< DMA2D Background CLUT Memory Address Register, Address offset: 0x30 */ + __IO uint32_t OPFCCR; /*!< DMA2D Output PFC Control Register, Address offset: 0x34 */ + __IO uint32_t OCOLR; /*!< DMA2D Output Color Register, Address offset: 0x38 */ + __IO uint32_t OMAR; /*!< DMA2D Output Memory Address Register, Address offset: 0x3C */ + __IO uint32_t OOR; /*!< DMA2D Output Offset Register, Address offset: 0x40 */ + __IO uint32_t NLR; /*!< DMA2D Number of Line Register, Address offset: 0x44 */ + __IO uint32_t LWR; /*!< DMA2D Line Watermark Register, Address offset: 0x48 */ + __IO uint32_t AMTCR; /*!< DMA2D AHB Master Timer Configuration Register, Address offset: 0x4C */ + uint32_t RESERVED[236]; /*!< Reserved, 0x50-0x3FF */ + __IO uint32_t FGCLUT[256]; /*!< DMA2D Foreground CLUT, Address offset:400-7FF */ + __IO uint32_t BGCLUT[256]; /*!< DMA2D Background CLUT, Address offset:800-BFF */ +} DMA2D_TypeDef; + + +/** + * @brief Ethernet MAC + */ +typedef struct +{ + __IO uint32_t MACCR; + __IO uint32_t MACECR; + __IO uint32_t MACPFR; + __IO uint32_t MACWTR; + __IO uint32_t MACHT0R; + __IO uint32_t MACHT1R; + uint32_t RESERVED1[14]; + __IO uint32_t MACVTR; + uint32_t RESERVED2; + __IO uint32_t MACVHTR; + uint32_t RESERVED3; + __IO uint32_t MACVIR; + __IO uint32_t MACIVIR; + uint32_t RESERVED4[2]; + __IO uint32_t MACTFCR; + uint32_t RESERVED5[7]; + __IO uint32_t MACRFCR; + uint32_t RESERVED6[7]; + __IO uint32_t MACISR; + __IO uint32_t MACIER; + __IO uint32_t MACRXTXSR; + uint32_t RESERVED7; + __IO uint32_t MACPCSR; + __IO uint32_t MACRWKPFR; + uint32_t RESERVED8[2]; + __IO uint32_t MACLCSR; + __IO uint32_t MACLTCR; + __IO uint32_t MACLETR; + __IO uint32_t MAC1USTCR; + uint32_t RESERVED9[12]; + __IO uint32_t MACVR; + __IO uint32_t MACDR; + uint32_t RESERVED10; + __IO uint32_t MACHWF0R; + __IO uint32_t MACHWF1R; + __IO uint32_t MACHWF2R; + uint32_t RESERVED11[54]; + __IO uint32_t MACMDIOAR; + __IO uint32_t MACMDIODR; + uint32_t RESERVED12[2]; + __IO uint32_t MACARPAR; + uint32_t RESERVED13[59]; + __IO uint32_t MACA0HR; + __IO uint32_t MACA0LR; + __IO uint32_t MACA1HR; + __IO uint32_t MACA1LR; + __IO uint32_t MACA2HR; + __IO uint32_t MACA2LR; + __IO uint32_t MACA3HR; + __IO uint32_t MACA3LR; + uint32_t RESERVED14[248]; + __IO uint32_t MMCCR; + __IO uint32_t MMCRIR; + __IO uint32_t MMCTIR; + __IO uint32_t MMCRIMR; + __IO uint32_t MMCTIMR; + uint32_t RESERVED15[14]; + __IO uint32_t MMCTSCGPR; + __IO uint32_t MMCTMCGPR; + uint32_t RESERVED16[5]; + __IO uint32_t MMCTPCGR; + uint32_t RESERVED17[10]; + __IO uint32_t MMCRCRCEPR; + __IO uint32_t MMCRAEPR; + uint32_t RESERVED18[10]; + __IO uint32_t MMCRUPGR; + uint32_t RESERVED19[9]; + __IO uint32_t MMCTLPIMSTR; + __IO uint32_t MMCTLPITCR; + __IO uint32_t MMCRLPIMSTR; + __IO uint32_t MMCRLPITCR; + uint32_t RESERVED20[65]; + __IO uint32_t MACL3L4C0R; + __IO uint32_t MACL4A0R; + uint32_t RESERVED21[2]; + __IO uint32_t MACL3A0R0R; + __IO uint32_t MACL3A1R0R; + __IO uint32_t MACL3A2R0R; + __IO uint32_t MACL3A3R0R; + uint32_t RESERVED22[4]; + __IO uint32_t MACL3L4C1R; + __IO uint32_t MACL4A1R; + uint32_t RESERVED23[2]; + __IO uint32_t MACL3A0R1R; + __IO uint32_t MACL3A1R1R; + __IO uint32_t MACL3A2R1R; + __IO uint32_t MACL3A3R1R; + uint32_t RESERVED24[108]; + __IO uint32_t MACTSCR; + __IO uint32_t MACSSIR; + __IO uint32_t MACSTSR; + __IO uint32_t MACSTNR; + __IO uint32_t MACSTSUR; + __IO uint32_t MACSTNUR; + __IO uint32_t MACTSAR; + uint32_t RESERVED25; + __IO uint32_t MACTSSR; + uint32_t RESERVED26[3]; + __IO uint32_t MACTTSSNR; + __IO uint32_t MACTTSSSR; + uint32_t RESERVED27[2]; + __IO uint32_t MACACR; + uint32_t RESERVED28; + __IO uint32_t MACATSNR; + __IO uint32_t MACATSSR; + __IO uint32_t MACTSIACR; + __IO uint32_t MACTSEACR; + __IO uint32_t MACTSICNR; + __IO uint32_t MACTSECNR; + uint32_t RESERVED29[4]; + __IO uint32_t MACPPSCR; + uint32_t RESERVED30[3]; + __IO uint32_t MACPPSTTSR; + __IO uint32_t MACPPSTTNR; + __IO uint32_t MACPPSIR; + __IO uint32_t MACPPSWR; + uint32_t RESERVED31[12]; + __IO uint32_t MACPOCR; + __IO uint32_t MACSPI0R; + __IO uint32_t MACSPI1R; + __IO uint32_t MACSPI2R; + __IO uint32_t MACLMIR; + uint32_t RESERVED32[11]; + __IO uint32_t MTLOMR; + uint32_t RESERVED33[7]; + __IO uint32_t MTLISR; + uint32_t RESERVED34[55]; + __IO uint32_t MTLTQOMR; + __IO uint32_t MTLTQUR; + __IO uint32_t MTLTQDR; + uint32_t RESERVED35[8]; + __IO uint32_t MTLQICSR; + __IO uint32_t MTLRQOMR; + __IO uint32_t MTLRQMPOCR; + __IO uint32_t MTLRQDR; + uint32_t RESERVED36[177]; + __IO uint32_t DMAMR; + __IO uint32_t DMASBMR; + __IO uint32_t DMAISR; + __IO uint32_t DMADSR; + uint32_t RESERVED37[60]; + __IO uint32_t DMACCR; + __IO uint32_t DMACTCR; + __IO uint32_t DMACRCR; + uint32_t RESERVED38[2]; + __IO uint32_t DMACTDLAR; + uint32_t RESERVED39; + __IO uint32_t DMACRDLAR; + __IO uint32_t DMACTDTPR; + uint32_t RESERVED40; + __IO uint32_t DMACRDTPR; + __IO uint32_t DMACTDRLR; + __IO uint32_t DMACRDRLR; + __IO uint32_t DMACIER; + __IO uint32_t DMACRIWTR; +__IO uint32_t DMACSFCSR; + uint32_t RESERVED41; + __IO uint32_t DMACCATDR; + uint32_t RESERVED42; + __IO uint32_t DMACCARDR; + uint32_t RESERVED43; + __IO uint32_t DMACCATBR; + uint32_t RESERVED44; + __IO uint32_t DMACCARBR; + __IO uint32_t DMACSR; +uint32_t RESERVED45[2]; +__IO uint32_t DMACMFCR; +}ETH_TypeDef; +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ +__IO uint32_t RTSR1; /*!< EXTI Rising trigger selection register, Address offset: 0x00 */ +__IO uint32_t FTSR1; /*!< EXTI Falling trigger selection register, Address offset: 0x04 */ +__IO uint32_t SWIER1; /*!< EXTI Software interrupt event register, Address offset: 0x08 */ +__IO uint32_t D3PMR1; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR1) Address offset: 0x0C */ +__IO uint32_t D3PCR1L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR1L) Address offset: 0x10 */ +__IO uint32_t D3PCR1H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR1H) Address offset: 0x14 */ +uint32_t RESERVED1[2]; /*!< Reserved, 0x18 to 0x1C */ +__IO uint32_t RTSR2; /*!< EXTI Rising trigger selection register, Address offset: 0x20 */ +__IO uint32_t FTSR2; /*!< EXTI Falling trigger selection register, Address offset: 0x24 */ +__IO uint32_t SWIER2; /*!< EXTI Software interrupt event register, Address offset: 0x28 */ +__IO uint32_t D3PMR2; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR2) Address offset: 0x2C */ +__IO uint32_t D3PCR2L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR2L) Address offset: 0x30 */ +__IO uint32_t D3PCR2H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR2H) Address offset: 0x34 */ +uint32_t RESERVED2[2]; /*!< Reserved, 0x38 to 0x3C */ +__IO uint32_t RTSR3; /*!< EXTI Rising trigger selection register, Address offset: 0x40 */ +__IO uint32_t FTSR3; /*!< EXTI Falling trigger selection register, Address offset: 0x44 */ +__IO uint32_t SWIER3; /*!< EXTI Software interrupt event register, Address offset: 0x48 */ +__IO uint32_t D3PMR3; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR3) Address offset: 0x4C */ +__IO uint32_t D3PCR3L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR3L) Address offset: 0x50 */ +__IO uint32_t D3PCR3H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR3H) Address offset: 0x54 */ +uint32_t RESERVED3[10]; /*!< Reserved, 0x58 to 0x7C */ +__IO uint32_t IMR1; /*!< EXTI Interrupt mask register, Address offset: 0x80 */ +__IO uint32_t EMR1; /*!< EXTI Event mask register, Address offset: 0x84 */ +__IO uint32_t PR1; /*!< EXTI Pending register, Address offset: 0x88 */ +uint32_t RESERVED4; /*!< Reserved, 0x8C */ +__IO uint32_t IMR2; /*!< EXTI Interrupt mask register, Address offset: 0x90 */ +__IO uint32_t EMR2; /*!< EXTI Event mask register, Address offset: 0x94 */ +__IO uint32_t PR2; /*!< EXTI Pending register, Address offset: 0x98 */ +uint32_t RESERVED5; /*!< Reserved, 0x9C */ +__IO uint32_t IMR3; /*!< EXTI Interrupt mask register, Address offset: 0xA0 */ +__IO uint32_t EMR3; /*!< EXTI Event mask register, Address offset: 0xA4 */ +__IO uint32_t PR3; /*!< EXTI Pending register, Address offset: 0xA8 */ +}EXTI_TypeDef; + +/** + * @brief This structure registers corresponds to EXTI_Typdef CPU1/CPU2 registers subset (IMRx, EMRx and PRx), allowing to define EXTI_D1/EXTI_D2 + * with rapid/common access to these IMRx, EMRx, PRx registers for CPU1 and CPU2. + * Note that EXTI_D1 and EXTI_D2 bases addresses are calculated to point to CPUx first register: + * IMR1 in case of EXTI_D1 that is addressing CPU1 (Coretx-M7) + * C2IMR1 in case of EXTI_D2 that is addressing CPU2 (Coretx-M4) + * Note: EXTI_D2 and corresponding C2IMRx, C2EMRx and C2PRx registers are available for Dual Core devices only + */ + +typedef struct +{ +__IO uint32_t IMR1; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ +__IO uint32_t EMR1; /*!< EXTI Event mask register, Address offset: 0x04 */ +__IO uint32_t PR1; /*!< EXTI Pending register, Address offset: 0x08 */ +uint32_t RESERVED1; /*!< Reserved, 0x0C */ +__IO uint32_t IMR2; /*!< EXTI Interrupt mask register, Address offset: 0x10 */ +__IO uint32_t EMR2; /*!< EXTI Event mask register, Address offset: 0x14 */ +__IO uint32_t PR2; /*!< EXTI Pending register, Address offset: 0x18 */ +uint32_t RESERVED2; /*!< Reserved, 0x1C */ +__IO uint32_t IMR3; /*!< EXTI Interrupt mask register, Address offset: 0x20 */ +__IO uint32_t EMR3; /*!< EXTI Event mask register, Address offset: 0x24 */ +__IO uint32_t PR3; /*!< EXTI Pending register, Address offset: 0x28 */ +}EXTI_Core_TypeDef; + + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR1; /*!< Flash Key Register for bank1, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< Flash Option Key Register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< Flash Control Register for bank1, Address offset: 0x0C */ + __IO uint32_t SR1; /*!< Flash Status Register for bank1, Address offset: 0x10 */ + __IO uint32_t CCR1; /*!< Flash Control Register for bank1, Address offset: 0x14 */ + __IO uint32_t OPTCR; /*!< Flash Option Control Register, Address offset: 0x18 */ + __IO uint32_t OPTSR_CUR; /*!< Flash Option Status Current Register, Address offset: 0x1C */ + __IO uint32_t OPTSR_PRG; /*!< Flash Option Status to Program Register, Address offset: 0x20 */ + __IO uint32_t OPTCCR; /*!< Flash Option Clear Control Register, Address offset: 0x24 */ + __IO uint32_t PRAR_CUR1; /*!< Flash Current Protection Address Register for bank1, Address offset: 0x28 */ + __IO uint32_t PRAR_PRG1; /*!< Flash Protection Address to Program Register for bank1, Address offset: 0x2C */ + __IO uint32_t SCAR_CUR1; /*!< Flash Current Secure Address Register for bank1, Address offset: 0x30 */ + __IO uint32_t SCAR_PRG1; /*!< Flash Secure Address to Program Register for bank1, Address offset: 0x34 */ + __IO uint32_t WPSN_CUR1; /*!< Flash Current Write Protection Register on bank1, Address offset: 0x38 */ + __IO uint32_t WPSN_PRG1; /*!< Flash Write Protection to Program Register on bank1, Address offset: 0x3C */ + __IO uint32_t BOOT_CUR; /*!< Flash Current Boot Address for Pelican Core Register, Address offset: 0x40 */ + __IO uint32_t BOOT_PRG; /*!< Flash Boot Address to Program for Pelican Core Register, Address offset: 0x44 */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x48 to 0x4C */ + __IO uint32_t CRCCR1; /*!< Flash CRC Control register For Bank1 Register , Address offset: 0x50 */ + __IO uint32_t CRCSADD1; /*!< Flash CRC Start Address Register for Bank1 , Address offset: 0x54 */ + __IO uint32_t CRCEADD1; /*!< Flash CRC End Address Register for Bank1 , Address offset: 0x58 */ + __IO uint32_t CRCDATA; /*!< Flash CRC Data Register for Bank1 , Address offset: 0x5C */ + __IO uint32_t ECC_FA1; /*!< Flash ECC Fail Address For Bank1 Register , Address offset: 0x60 */ + uint32_t RESERVED[3]; /*!< Reserved, 0x64 to 0x6C */ + __IO uint32_t OPTSR2_CUR; /*!< Flash Option Status Current Register 2, Address offset: 0x70 */ + __IO uint32_t OPTSR2_PRG; /*!< Flash Option Status to Program Register 2, Address offset: 0x74 */ +} FLASH_TypeDef; + +/** + * @brief Filter and Mathematical ACcelerator + */ +typedef struct +{ + __IO uint32_t X1BUFCFG; /*!< FMAC X1 Buffer Configuration register, Address offset: 0x00 */ + __IO uint32_t X2BUFCFG; /*!< FMAC X2 Buffer Configuration register, Address offset: 0x04 */ + __IO uint32_t YBUFCFG; /*!< FMAC Y Buffer Configuration register, Address offset: 0x08 */ + __IO uint32_t PARAM; /*!< FMAC Parameter register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FMAC Control register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< FMAC Status register, Address offset: 0x14 */ + __IO uint32_t WDATA; /*!< FMAC Write Data register, Address offset: 0x18 */ + __IO uint32_t RDATA; /*!< FMAC Read Data register, Address offset: 0x1C */ +} FMAC_TypeDef; + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank2 + */ + +typedef struct +{ + __IO uint32_t PCR2; /*!< NAND Flash control register 2, Address offset: 0x60 */ + __IO uint32_t SR2; /*!< NAND Flash FIFO status and interrupt register 2, Address offset: 0x64 */ + __IO uint32_t PMEM2; /*!< NAND Flash Common memory space timing register 2, Address offset: 0x68 */ + __IO uint32_t PATT2; /*!< NAND Flash Attribute memory space timing register 2, Address offset: 0x6C */ + uint32_t RESERVED0; /*!< Reserved, 0x70 */ + __IO uint32_t ECCR2; /*!< NAND Flash ECC result registers 2, Address offset: 0x74 */ +} FMC_Bank2_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register 3, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register 3, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register 3, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register 3, Address offset: 0x8C */ + uint32_t RESERVED; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers 3, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief Flexible Memory Controller Bank5 and 6 + */ + + +typedef struct +{ + __IO uint32_t SDCR[2]; /*!< SDRAM Control registers , Address offset: 0x140-0x144 */ + __IO uint32_t SDTR[2]; /*!< SDRAM Timing registers , Address offset: 0x148-0x14C */ + __IO uint32_t SDCMR; /*!< SDRAM Command Mode register, Address offset: 0x150 */ + __IO uint32_t SDRTR; /*!< SDRAM Refresh Timer register, Address offset: 0x154 */ + __IO uint32_t SDSR; /*!< SDRAM Status register, Address offset: 0x158 */ +} FMC_Bank5_6_TypeDef; + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief Operational Amplifier (OPAMP) + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< OPAMP control/status register, Address offset: 0x00 */ + __IO uint32_t OTR; /*!< OPAMP offset trimming register for normal mode, Address offset: 0x04 */ + __IO uint32_t HSOTR; /*!< OPAMP offset trimming register for high speed mode, Address offset: 0x08 */ +} OPAMP_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x00 */ + __IO uint32_t PMCR; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + __IO uint32_t CFGR; /*!< SYSCFG configuration registers, Address offset: 0x18 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t CCCSR; /*!< SYSCFG compensation cell control/status register, Address offset: 0x20 */ + __IO uint32_t CCVR; /*!< SYSCFG compensation cell value register, Address offset: 0x24 */ + __IO uint32_t CCCR; /*!< SYSCFG compensation cell code register, Address offset: 0x28 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x2C */ + __IO uint32_t ADC2ALT; /*!< ADC2 internal input alternate connection register, Address offset: 0x30 */ + uint32_t RESERVED4[60]; /*!< Reserved, 0x34-0x120 */ + __IO uint32_t PKGR; /*!< SYSCFG package register, Address offset: 0x124 */ + uint32_t RESERVED5[118]; /*!< Reserved, 0x128-0x2FC */ + __IO uint32_t UR0; /*!< SYSCFG user register 0, Address offset: 0x300 */ + __IO uint32_t UR1; /*!< SYSCFG user register 1, Address offset: 0x304 */ + __IO uint32_t UR2; /*!< SYSCFG user register 2, Address offset: 0x308 */ + __IO uint32_t UR3; /*!< SYSCFG user register 3, Address offset: 0x30C */ + __IO uint32_t UR4; /*!< SYSCFG user register 4, Address offset: 0x310 */ + __IO uint32_t UR5; /*!< SYSCFG user register 5, Address offset: 0x314 */ + __IO uint32_t UR6; /*!< SYSCFG user register 6, Address offset: 0x318 */ + __IO uint32_t UR7; /*!< SYSCFG user register 7, Address offset: 0x31C */ + uint32_t RESERVED6[3]; /*!< Reserved, Address offset: 0x320-0x328 */ + __IO uint32_t UR11; /*!< SYSCFG user register 11, Address offset: 0x32C */ + __IO uint32_t UR12; /*!< SYSCFG user register 12, Address offset: 0x330 */ + __IO uint32_t UR13; /*!< SYSCFG user register 13, Address offset: 0x334 */ + __IO uint32_t UR14; /*!< SYSCFG user register 14, Address offset: 0x338 */ + __IO uint32_t UR15; /*!< SYSCFG user register 15, Address offset: 0x33C */ + __IO uint32_t UR16; /*!< SYSCFG user register 16, Address offset: 0x340 */ + __IO uint32_t UR17; /*!< SYSCFG user register 17, Address offset: 0x344 */ + __IO uint32_t UR18; /*!< SYSCFG user register 18, Address offset: 0x348 */ + +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + + +/** + * @brief LCD-TFT Display Controller + */ + +typedef struct +{ + uint32_t RESERVED0[2]; /*!< Reserved, 0x00-0x04 */ + __IO uint32_t SSCR; /*!< LTDC Synchronization Size Configuration Register, Address offset: 0x08 */ + __IO uint32_t BPCR; /*!< LTDC Back Porch Configuration Register, Address offset: 0x0C */ + __IO uint32_t AWCR; /*!< LTDC Active Width Configuration Register, Address offset: 0x10 */ + __IO uint32_t TWCR; /*!< LTDC Total Width Configuration Register, Address offset: 0x14 */ + __IO uint32_t GCR; /*!< LTDC Global Control Register, Address offset: 0x18 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x1C-0x20 */ + __IO uint32_t SRCR; /*!< LTDC Shadow Reload Configuration Register, Address offset: 0x24 */ + uint32_t RESERVED2[1]; /*!< Reserved, 0x28 */ + __IO uint32_t BCCR; /*!< LTDC Background Color Configuration Register, Address offset: 0x2C */ + uint32_t RESERVED3[1]; /*!< Reserved, 0x30 */ + __IO uint32_t IER; /*!< LTDC Interrupt Enable Register, Address offset: 0x34 */ + __IO uint32_t ISR; /*!< LTDC Interrupt Status Register, Address offset: 0x38 */ + __IO uint32_t ICR; /*!< LTDC Interrupt Clear Register, Address offset: 0x3C */ + __IO uint32_t LIPCR; /*!< LTDC Line Interrupt Position Configuration Register, Address offset: 0x40 */ + __IO uint32_t CPSR; /*!< LTDC Current Position Status Register, Address offset: 0x44 */ + __IO uint32_t CDSR; /*!< LTDC Current Display Status Register, Address offset: 0x48 */ +} LTDC_TypeDef; + +/** + * @brief LCD-TFT Display layer x Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< LTDC Layerx Control Register Address offset: 0x84 */ + __IO uint32_t WHPCR; /*!< LTDC Layerx Window Horizontal Position Configuration Register Address offset: 0x88 */ + __IO uint32_t WVPCR; /*!< LTDC Layerx Window Vertical Position Configuration Register Address offset: 0x8C */ + __IO uint32_t CKCR; /*!< LTDC Layerx Color Keying Configuration Register Address offset: 0x90 */ + __IO uint32_t PFCR; /*!< LTDC Layerx Pixel Format Configuration Register Address offset: 0x94 */ + __IO uint32_t CACR; /*!< LTDC Layerx Constant Alpha Configuration Register Address offset: 0x98 */ + __IO uint32_t DCCR; /*!< LTDC Layerx Default Color Configuration Register Address offset: 0x9C */ + __IO uint32_t BFCR; /*!< LTDC Layerx Blending Factors Configuration Register Address offset: 0xA0 */ + uint32_t RESERVED0[2]; /*!< Reserved */ + __IO uint32_t CFBAR; /*!< LTDC Layerx Color Frame Buffer Address Register Address offset: 0xAC */ + __IO uint32_t CFBLR; /*!< LTDC Layerx Color Frame Buffer Length Register Address offset: 0xB0 */ + __IO uint32_t CFBLNR; /*!< LTDC Layerx ColorFrame Buffer Line Number Register Address offset: 0xB4 */ + uint32_t RESERVED1[3]; /*!< Reserved */ + __IO uint32_t CLUTWR; /*!< LTDC Layerx CLUT Write Register Address offset: 0x144 */ + +} LTDC_Layer_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CSR1; /*!< PWR power control status register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x08 */ + __IO uint32_t CR3; /*!< PWR power control register 3, Address offset: 0x0C */ + __IO uint32_t CPUCR; /*!< PWR CPU control register, Address offset: 0x10 */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t D3CR; /*!< PWR D3 domain control register, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t WKUPCR; /*!< PWR wakeup clear register, Address offset: 0x20 */ + __IO uint32_t WKUPFR; /*!< PWR wakeup flag register, Address offset: 0x24 */ + __IO uint32_t WKUPEPR; /*!< PWR wakeup enable and polarity register, Address offset: 0x28 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t HSICFGR; /*!< HSI Clock Calibration Register, Address offset: 0x04 */ + __IO uint32_t CRRCR; /*!< Clock Recovery RC Register, Address offset: 0x08 */ + __IO uint32_t CSICFGR; /*!< CSI Clock Calibration Register, Address offset: 0x0C */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x10 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t D1CFGR; /*!< RCC Domain 1 configuration register, Address offset: 0x18 */ + __IO uint32_t D2CFGR; /*!< RCC Domain 2 configuration register, Address offset: 0x1C */ + __IO uint32_t D3CFGR; /*!< RCC Domain 3 configuration register, Address offset: 0x20 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x24 */ + __IO uint32_t PLLCKSELR; /*!< RCC PLLs Clock Source Selection Register, Address offset: 0x28 */ + __IO uint32_t PLLCFGR; /*!< RCC PLLs Configuration Register, Address offset: 0x2C */ + __IO uint32_t PLL1DIVR; /*!< RCC PLL1 Dividers Configuration Register, Address offset: 0x30 */ + __IO uint32_t PLL1FRACR; /*!< RCC PLL1 Fractional Divider Configuration Register, Address offset: 0x34 */ + __IO uint32_t PLL2DIVR; /*!< RCC PLL2 Dividers Configuration Register, Address offset: 0x38 */ + __IO uint32_t PLL2FRACR; /*!< RCC PLL2 Fractional Divider Configuration Register, Address offset: 0x3C */ + __IO uint32_t PLL3DIVR; /*!< RCC PLL3 Dividers Configuration Register, Address offset: 0x40 */ + __IO uint32_t PLL3FRACR; /*!< RCC PLL3 Fractional Divider Configuration Register, Address offset: 0x44 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x48 */ + __IO uint32_t D1CCIPR; /*!< RCC Domain 1 Kernel Clock Configuration Register Address offset: 0x4C */ + __IO uint32_t D2CCIP1R; /*!< RCC Domain 2 Kernel Clock Configuration Register Address offset: 0x50 */ + __IO uint32_t D2CCIP2R; /*!< RCC Domain 2 Kernel Clock Configuration Register Address offset: 0x54 */ + __IO uint32_t D3CCIPR; /*!< RCC Domain 3 Kernel Clock Configuration Register Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x5C */ + __IO uint32_t CIER; /*!< RCC Clock Source Interrupt Enable Register Address offset: 0x60 */ + __IO uint32_t CIFR; /*!< RCC Clock Source Interrupt Flag Register Address offset: 0x64 */ + __IO uint32_t CICR; /*!< RCC Clock Source Interrupt Clear Register Address offset: 0x68 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x6C */ + __IO uint32_t BDCR; /*!< RCC Vswitch Backup Domain Control Register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x78 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x7C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x80 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x84 */ + __IO uint32_t AHB4RSTR; /*!< RCC AHB4 peripheral reset register, Address offset: 0x88 */ + __IO uint32_t APB3RSTR; /*!< RCC APB3 peripheral reset register, Address offset: 0x8C */ + __IO uint32_t APB1LRSTR; /*!< RCC APB1 peripheral reset Low Word register, Address offset: 0x90 */ + __IO uint32_t APB1HRSTR; /*!< RCC APB1 peripheral reset High Word register, Address offset: 0x94 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x98 */ + __IO uint32_t APB4RSTR; /*!< RCC APB4 peripheral reset register, Address offset: 0x9C */ + __IO uint32_t GCR; /*!< RCC RCC Global Control Register, Address offset: 0xA0 */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0xA4 */ + __IO uint32_t D3AMR; /*!< RCC Domain 3 Autonomous Mode Register, Address offset: 0xA8 */ + uint32_t RESERVED11[9]; /*!< Reserved, 0xAC-0xCC Address offset: 0xAC */ + __IO uint32_t RSR; /*!< RCC Reset status register, Address offset: 0xD0 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0xD4 */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0xD8 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0xDC */ + __IO uint32_t AHB4ENR; /*!< RCC AHB4 peripheral clock register, Address offset: 0xE0 */ + __IO uint32_t APB3ENR; /*!< RCC APB3 peripheral clock register, Address offset: 0xE4 */ + __IO uint32_t APB1LENR; /*!< RCC APB1 peripheral clock Low Word register, Address offset: 0xE8 */ + __IO uint32_t APB1HENR; /*!< RCC APB1 peripheral clock High Word register, Address offset: 0xEC */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock register, Address offset: 0xF0 */ + __IO uint32_t APB4ENR; /*!< RCC APB4 peripheral clock register, Address offset: 0xF4 */ + uint32_t RESERVED12; /*!< Reserved, Address offset: 0xF8 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral sleep clock register, Address offset: 0xFC */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral sleep clock register, Address offset: 0x100 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral sleep clock register, Address offset: 0x104 */ + __IO uint32_t AHB4LPENR; /*!< RCC AHB4 peripheral sleep clock register, Address offset: 0x108 */ + __IO uint32_t APB3LPENR; /*!< RCC APB3 peripheral sleep clock register, Address offset: 0x10C */ + __IO uint32_t APB1LLPENR; /*!< RCC APB1 peripheral sleep clock Low Word register, Address offset: 0x110 */ + __IO uint32_t APB1HLPENR; /*!< RCC APB1 peripheral sleep clock High Word register, Address offset: 0x114 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral sleep clock register, Address offset: 0x118 */ + __IO uint32_t APB4LPENR; /*!< RCC APB4 peripheral sleep clock register, Address offset: 0x11C */ + uint32_t RESERVED13[4]; /*!< Reserved, 0x120-0x12C Address offset: 0x120 */ + +} RCC_TypeDef; + + +/** + * @brief Real-Time Clock + */ +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAMPCR; /*!< RTC tamper configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x48 */ + __IO uint32_t OR; /*!< RTC option register, Address offset: 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 0, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ + __IO uint32_t BKP20R; /*!< RTC backup register 20, Address offset: 0xA0 */ + __IO uint32_t BKP21R; /*!< RTC backup register 21, Address offset: 0xA4 */ + __IO uint32_t BKP22R; /*!< RTC backup register 22, Address offset: 0xA8 */ + __IO uint32_t BKP23R; /*!< RTC backup register 23, Address offset: 0xAC */ + __IO uint32_t BKP24R; /*!< RTC backup register 24, Address offset: 0xB0 */ + __IO uint32_t BKP25R; /*!< RTC backup register 25, Address offset: 0xB4 */ + __IO uint32_t BKP26R; /*!< RTC backup register 26, Address offset: 0xB8 */ + __IO uint32_t BKP27R; /*!< RTC backup register 27, Address offset: 0xBC */ + __IO uint32_t BKP28R; /*!< RTC backup register 28, Address offset: 0xC0 */ + __IO uint32_t BKP29R; /*!< RTC backup register 29, Address offset: 0xC4 */ + __IO uint32_t BKP30R; /*!< RTC backup register 30, Address offset: 0xC8 */ + __IO uint32_t BKP31R; /*!< RTC backup register 31, Address offset: 0xCC */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ + uint32_t RESERVED0[16]; /*!< Reserved, 0x04 - 0x43 */ + __IO uint32_t PDMCR; /*!< SAI PDM control register, Address offset: 0x44 */ + __IO uint32_t PDMDLY; /*!< SAI PDM delay register, Address offset: 0x48 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SPDIF-RX Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< Control register, Address offset: 0x00 */ + __IO uint32_t IMR; /*!< Interrupt mask register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< Status register, Address offset: 0x08 */ + __IO uint32_t IFCR; /*!< Interrupt Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DR; /*!< Data input register, Address offset: 0x10 */ + __IO uint32_t CSR; /*!< Channel Status register, Address offset: 0x14 */ + __IO uint32_t DIR; /*!< Debug Information register, Address offset: 0x18 */ + uint32_t RESERVED2; /*!< Reserved, 0x1A */ +} SPDIFRX_TypeDef; + + +/** + * @brief Secure digital input/output Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDMMC power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDMMC clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDMMC argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDMMC command register, Address offset: 0x0C */ + __I uint32_t RESPCMD; /*!< SDMMC command response register, Address offset: 0x10 */ + __I uint32_t RESP1; /*!< SDMMC response 1 register, Address offset: 0x14 */ + __I uint32_t RESP2; /*!< SDMMC response 2 register, Address offset: 0x18 */ + __I uint32_t RESP3; /*!< SDMMC response 3 register, Address offset: 0x1C */ + __I uint32_t RESP4; /*!< SDMMC response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDMMC data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDMMC data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDMMC data control register, Address offset: 0x2C */ + __I uint32_t DCOUNT; /*!< SDMMC data counter register, Address offset: 0x30 */ + __I uint32_t STA; /*!< SDMMC status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDMMC interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDMMC mask register, Address offset: 0x3C */ + __IO uint32_t ACKTIME; /*!< SDMMC Acknowledgement timer register, Address offset: 0x40 */ + uint32_t RESERVED0[3]; /*!< Reserved, 0x44 - 0x4C - 0x4C */ + __IO uint32_t IDMACTRL; /*!< SDMMC DMA control register, Address offset: 0x50 */ + __IO uint32_t IDMABSIZE; /*!< SDMMC DMA buffer size register, Address offset: 0x54 */ + __IO uint32_t IDMABASE0; /*!< SDMMC DMA buffer 0 base address register, Address offset: 0x58 */ + __IO uint32_t IDMABASE1; /*!< SDMMC DMA buffer 1 base address register, Address offset: 0x5C */ + uint32_t RESERVED1[8]; /*!< Reserved, 0x60-0x7C */ + __IO uint32_t FIFO; /*!< SDMMC data FIFO register, Address offset: 0x80 */ + uint32_t RESERVED2[222]; /*!< Reserved, 0x84-0x3F8 */ + __IO uint32_t IPVR; /*!< SDMMC data FIFO register, Address offset: 0x3FC */ +} SDMMC_TypeDef; + + +/** + * @brief Delay Block DLYB + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DELAY BLOCK control register, Address offset: 0x00 */ + __IO uint32_t CFGR; /*!< DELAY BLOCK configuration register, Address offset: 0x04 */ +} DLYB_TypeDef; + +/** + * @brief HW Semaphore HSEM + */ + +typedef struct +{ + __IO uint32_t R[32]; /*!< 2-step write lock and read back registers, Address offset: 00h-7Ch */ + __IO uint32_t RLR[32]; /*!< 1-step read lock registers, Address offset: 80h-FCh */ + __IO uint32_t C1IER; /*!< HSEM Interrupt enable register , Address offset: 100h */ + __IO uint32_t C1ICR; /*!< HSEM Interrupt clear register , Address offset: 104h */ + __IO uint32_t C1ISR; /*!< HSEM Interrupt Status register , Address offset: 108h */ + __IO uint32_t C1MISR; /*!< HSEM Interrupt Masked Status register , Address offset: 10Ch */ + uint32_t Reserved[12]; /* Reserved Address offset: 110h-13Ch */ + __IO uint32_t CR; /*!< HSEM Semaphore clear register , Address offset: 140h */ + __IO uint32_t KEYR; /*!< HSEM Semaphore clear key register , Address offset: 144h */ + +} HSEM_TypeDef; + +typedef struct +{ + __IO uint32_t IER; /*!< HSEM interrupt enable register , Address offset: 0h */ + __IO uint32_t ICR; /*!< HSEM interrupt clear register , Address offset: 4h */ + __IO uint32_t ISR; /*!< HSEM interrupt status register , Address offset: 8h */ + __IO uint32_t MISR; /*!< HSEM masked interrupt status register , Address offset: Ch */ +} HSEM_Common_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI/I2S Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */ + __IO uint32_t CFG1; /*!< SPI Configuration register 1, Address offset: 0x08 */ + __IO uint32_t CFG2; /*!< SPI Configuration register 2, Address offset: 0x0C */ + __IO uint32_t IER; /*!< SPI/I2S Interrupt Enable register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< SPI/I2S Status register, Address offset: 0x14 */ + __IO uint32_t IFCR; /*!< SPI/I2S Interrupt/Status flags clear register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t TXDR; /*!< SPI/I2S Transmit data register, Address offset: 0x20 */ + uint32_t RESERVED1[3]; /*!< Reserved, 0x24-0x2C */ + __IO uint32_t RXDR; /*!< SPI/I2S Receive data register, Address offset: 0x30 */ + uint32_t RESERVED2[3]; /*!< Reserved, 0x34-0x3C */ + __IO uint32_t CRCPOLY; /*!< SPI CRC Polynomial register, Address offset: 0x40 */ + __IO uint32_t TXCRC; /*!< SPI Transmitter CRC register, Address offset: 0x44 */ + __IO uint32_t RXCRC; /*!< SPI Receiver CRC register, Address offset: 0x48 */ + __IO uint32_t UDRDR; /*!< SPI Underrun data register, Address offset: 0x4C */ + __IO uint32_t I2SCFGR; /*!< I2S Configuration register, Address offset: 0x50 */ + +} SPI_TypeDef; + +/** + * @brief DTS + */ +typedef struct +{ + __IO uint32_t CFGR1; /*!< DTS configuration register, Address offset: 0x00 */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x04 */ + __IO uint32_t T0VALR1; /*!< DTS T0 Value register, Address offset: 0x08 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x0C */ + __IO uint32_t RAMPVALR; /*!< DTS Ramp value register, Address offset: 0x10 */ + __IO uint32_t ITR1; /*!< DTS Interrupt threshold register, Address offset: 0x14 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x18 */ + __IO uint32_t DR; /*!< DTS data register, Address offset: 0x1C */ + __IO uint32_t SR; /*!< DTS status register Address offset: 0x20 */ + __IO uint32_t ITENR; /*!< DTS Interrupt enable register, Address offset: 0x24 */ + __IO uint32_t ICIFR; /*!< DTS Clear Interrupt flag register, Address offset: 0x28 */ + __IO uint32_t OR; /*!< DTS option register 1, Address offset: 0x2C */ +} +DTS_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + uint32_t RESERVED1; /*!< Reserved, 0x50 */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */ + __IO uint32_t CCR5; /*!< TIM capture/compare register5, Address offset: 0x58 */ + __IO uint32_t CCR6; /*!< TIM capture/compare register6, Address offset: 0x5C */ + __IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */ + __IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */ + __IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x68 */ +} TIM_TypeDef; + +/** + * @brief LPTIMIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + uint32_t RESERVED1; /*!< Reserved, 0x20 */ + __IO uint32_t CFGR2; /*!< LPTIM Configuration register, Address offset: 0x24 */ +} LPTIM_TypeDef; + +/** + * @brief Comparator + */ +typedef struct +{ + __IO uint32_t SR; /*!< Comparator status register, Address offset: 0x00 */ + __IO uint32_t ICFR; /*!< Comparator interrupt clear flag register, Address offset: 0x04 */ + __IO uint32_t OR; /*!< Comparator option register, Address offset: 0x08 */ +} COMPOPT_TypeDef; + +typedef struct +{ + __IO uint32_t CFGR; /*!< Comparator configuration register , Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CFGR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ + __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ +} USART_TypeDef; + +/** + * @brief Single Wire Protocol Master Interface SPWMI + */ +typedef struct +{ + __IO uint32_t CR; /*!< SWPMI Configuration/Control register, Address offset: 0x00 */ + __IO uint32_t BRR; /*!< SWPMI bitrate register, Address offset: 0x04 */ + uint32_t RESERVED1; /*!< Reserved, 0x08 */ + __IO uint32_t ISR; /*!< SWPMI Interrupt and Status register, Address offset: 0x0C */ + __IO uint32_t ICR; /*!< SWPMI Interrupt Flag Clear register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< SWPMI Interrupt Enable register, Address offset: 0x14 */ + __IO uint32_t RFL; /*!< SWPMI Receive Frame Length register, Address offset: 0x18 */ + __IO uint32_t TDR; /*!< SWPMI Transmit data register, Address offset: 0x1C */ + __IO uint32_t RDR; /*!< SWPMI Receive data register, Address offset: 0x20 */ + __IO uint32_t OR; /*!< SWPMI Option register, Address offset: 0x24 */ +} SWPMI_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RAM_ECC_Specific_Registers + */ +typedef struct +{ + __IO uint32_t CR; /*!< RAMECC monitor configuration register */ + __IO uint32_t SR; /*!< RAMECC monitor status register */ + __IO uint32_t FAR; /*!< RAMECC monitor failing address register */ + __IO uint32_t FDRL; /*!< RAMECC monitor failing data low register */ + __IO uint32_t FDRH; /*!< RAMECC monitor failing data high register */ + __IO uint32_t FECR; /*!< RAMECC monitor failing ECC error code register */ +} RAMECC_MonitorTypeDef; + +typedef struct +{ + __IO uint32_t IER; /*!< RAMECC interrupt enable register */ +} RAMECC_TypeDef; +/** + * @} + */ + + +/** + * @brief Crypto Processor + */ + +typedef struct +{ + __IO uint32_t CR; /*!< CRYP control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< CRYP status register, Address offset: 0x04 */ + __IO uint32_t DIN; /*!< CRYP data input register, Address offset: 0x08 */ + __IO uint32_t DOUT; /*!< CRYP data output register, Address offset: 0x0C */ + __IO uint32_t DMACR; /*!< CRYP DMA control register, Address offset: 0x10 */ + __IO uint32_t IMSCR; /*!< CRYP interrupt mask set/clear register, Address offset: 0x14 */ + __IO uint32_t RISR; /*!< CRYP raw interrupt status register, Address offset: 0x18 */ + __IO uint32_t MISR; /*!< CRYP masked interrupt status register, Address offset: 0x1C */ + __IO uint32_t K0LR; /*!< CRYP key left register 0, Address offset: 0x20 */ + __IO uint32_t K0RR; /*!< CRYP key right register 0, Address offset: 0x24 */ + __IO uint32_t K1LR; /*!< CRYP key left register 1, Address offset: 0x28 */ + __IO uint32_t K1RR; /*!< CRYP key right register 1, Address offset: 0x2C */ + __IO uint32_t K2LR; /*!< CRYP key left register 2, Address offset: 0x30 */ + __IO uint32_t K2RR; /*!< CRYP key right register 2, Address offset: 0x34 */ + __IO uint32_t K3LR; /*!< CRYP key left register 3, Address offset: 0x38 */ + __IO uint32_t K3RR; /*!< CRYP key right register 3, Address offset: 0x3C */ + __IO uint32_t IV0LR; /*!< CRYP initialization vector left-word register 0, Address offset: 0x40 */ + __IO uint32_t IV0RR; /*!< CRYP initialization vector right-word register 0, Address offset: 0x44 */ + __IO uint32_t IV1LR; /*!< CRYP initialization vector left-word register 1, Address offset: 0x48 */ + __IO uint32_t IV1RR; /*!< CRYP initialization vector right-word register 1, Address offset: 0x4C */ + __IO uint32_t CSGCMCCM0R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 0, Address offset: 0x50 */ + __IO uint32_t CSGCMCCM1R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 1, Address offset: 0x54 */ + __IO uint32_t CSGCMCCM2R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 2, Address offset: 0x58 */ + __IO uint32_t CSGCMCCM3R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 3, Address offset: 0x5C */ + __IO uint32_t CSGCMCCM4R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 4, Address offset: 0x60 */ + __IO uint32_t CSGCMCCM5R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 5, Address offset: 0x64 */ + __IO uint32_t CSGCMCCM6R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 6, Address offset: 0x68 */ + __IO uint32_t CSGCMCCM7R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 7, Address offset: 0x6C */ + __IO uint32_t CSGCM0R; /*!< CRYP GCM/GMAC context swap register 0, Address offset: 0x70 */ + __IO uint32_t CSGCM1R; /*!< CRYP GCM/GMAC context swap register 1, Address offset: 0x74 */ + __IO uint32_t CSGCM2R; /*!< CRYP GCM/GMAC context swap register 2, Address offset: 0x78 */ + __IO uint32_t CSGCM3R; /*!< CRYP GCM/GMAC context swap register 3, Address offset: 0x7C */ + __IO uint32_t CSGCM4R; /*!< CRYP GCM/GMAC context swap register 4, Address offset: 0x80 */ + __IO uint32_t CSGCM5R; /*!< CRYP GCM/GMAC context swap register 5, Address offset: 0x84 */ + __IO uint32_t CSGCM6R; /*!< CRYP GCM/GMAC context swap register 6, Address offset: 0x88 */ + __IO uint32_t CSGCM7R; /*!< CRYP GCM/GMAC context swap register 7, Address offset: 0x8C */ +} CRYP_TypeDef; + +/** + * @brief HASH + */ + +typedef struct +{ + __IO uint32_t CR; /*!< HASH control register, Address offset: 0x00 */ + __IO uint32_t DIN; /*!< HASH data input register, Address offset: 0x04 */ + __IO uint32_t STR; /*!< HASH start register, Address offset: 0x08 */ + __IO uint32_t HR[5]; /*!< HASH digest registers, Address offset: 0x0C-0x1C */ + __IO uint32_t IMR; /*!< HASH interrupt enable register, Address offset: 0x20 */ + __IO uint32_t SR; /*!< HASH status register, Address offset: 0x24 */ + uint32_t RESERVED[52]; /*!< Reserved, 0x28-0xF4 */ + __IO uint32_t CSR[54]; /*!< HASH context swap registers, Address offset: 0x0F8-0x1CC */ +} HASH_TypeDef; + +/** + * @brief HASH_DIGEST + */ + +typedef struct +{ + __IO uint32_t HR[8]; /*!< HASH digest registers, Address offset: 0x310-0x32C */ +} HASH_DIGEST_TypeDef; + + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ + uint32_t RESERVED; + __IO uint32_t HTCR; /*!< RNG health test configuration register, Address offset: 0x10 */ +} RNG_TypeDef; + +/** + * @brief MDIOS + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t WRFR; + __IO uint32_t CWRFR; + __IO uint32_t RDFR; + __IO uint32_t CRDFR; + __IO uint32_t SR; + __IO uint32_t CLRFR; + uint32_t RESERVED[57]; + __IO uint32_t DINR0; + __IO uint32_t DINR1; + __IO uint32_t DINR2; + __IO uint32_t DINR3; + __IO uint32_t DINR4; + __IO uint32_t DINR5; + __IO uint32_t DINR6; + __IO uint32_t DINR7; + __IO uint32_t DINR8; + __IO uint32_t DINR9; + __IO uint32_t DINR10; + __IO uint32_t DINR11; + __IO uint32_t DINR12; + __IO uint32_t DINR13; + __IO uint32_t DINR14; + __IO uint32_t DINR15; + __IO uint32_t DINR16; + __IO uint32_t DINR17; + __IO uint32_t DINR18; + __IO uint32_t DINR19; + __IO uint32_t DINR20; + __IO uint32_t DINR21; + __IO uint32_t DINR22; + __IO uint32_t DINR23; + __IO uint32_t DINR24; + __IO uint32_t DINR25; + __IO uint32_t DINR26; + __IO uint32_t DINR27; + __IO uint32_t DINR28; + __IO uint32_t DINR29; + __IO uint32_t DINR30; + __IO uint32_t DINR31; + __IO uint32_t DOUTR0; + __IO uint32_t DOUTR1; + __IO uint32_t DOUTR2; + __IO uint32_t DOUTR3; + __IO uint32_t DOUTR4; + __IO uint32_t DOUTR5; + __IO uint32_t DOUTR6; + __IO uint32_t DOUTR7; + __IO uint32_t DOUTR8; + __IO uint32_t DOUTR9; + __IO uint32_t DOUTR10; + __IO uint32_t DOUTR11; + __IO uint32_t DOUTR12; + __IO uint32_t DOUTR13; + __IO uint32_t DOUTR14; + __IO uint32_t DOUTR15; + __IO uint32_t DOUTR16; + __IO uint32_t DOUTR17; + __IO uint32_t DOUTR18; + __IO uint32_t DOUTR19; + __IO uint32_t DOUTR20; + __IO uint32_t DOUTR21; + __IO uint32_t DOUTR22; + __IO uint32_t DOUTR23; + __IO uint32_t DOUTR24; + __IO uint32_t DOUTR25; + __IO uint32_t DOUTR26; + __IO uint32_t DOUTR27; + __IO uint32_t DOUTR28; + __IO uint32_t DOUTR29; + __IO uint32_t DOUTR30; + __IO uint32_t DOUTR31; +} MDIOS_TypeDef; + + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + __IO uint32_t GSNPSID; /* USB_OTG core ID 040h*/ + __IO uint32_t GHWCFG1; /* User HW config1 044h*/ + __IO uint32_t GHWCFG2; /* User HW config2 048h*/ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + __IO uint32_t GPWRDN; /*!< Power Down Register 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + __IO uint32_t GADPCTL; /*!< ADP Timer, Control and Status Register 60Ch */ + uint32_t Reserved43[39]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; +/** + * @} + */ + +/** + * @brief OCTO Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< OCTOSPI Control register, Address offset: 0x000 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x004 */ + __IO uint32_t DCR1; /*!< OCTOSPI Device Configuration register 1, Address offset: 0x008 */ + __IO uint32_t DCR2; /*!< OCTOSPI Device Configuration register 2, Address offset: 0x00C */ + __IO uint32_t DCR3; /*!< OCTOSPI Device Configuration register 3, Address offset: 0x010 */ + __IO uint32_t DCR4; /*!< OCTOSPI Device Configuration register 4, Address offset: 0x014 */ + uint32_t RESERVED1[2]; /*!< Reserved, Address offset: 0x018-0x01C */ + __IO uint32_t SR; /*!< OCTOSPI Status register, Address offset: 0x020 */ + __IO uint32_t FCR; /*!< OCTOSPI Flag Clear register, Address offset: 0x024 */ + uint32_t RESERVED2[6]; /*!< Reserved, Address offset: 0x028-0x03C */ + __IO uint32_t DLR; /*!< OCTOSPI Data Length register, Address offset: 0x040 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x044 */ + __IO uint32_t AR; /*!< OCTOSPI Address register, Address offset: 0x048 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x04C */ + __IO uint32_t DR; /*!< OCTOSPI Data register, Address offset: 0x050 */ + uint32_t RESERVED5[11]; /*!< Reserved, Address offset: 0x054-0x07C */ + __IO uint32_t PSMKR; /*!< OCTOSPI Polling Status Mask register, Address offset: 0x080 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x084 */ + __IO uint32_t PSMAR; /*!< OCTOSPI Polling Status Match register, Address offset: 0x088 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x08C */ + __IO uint32_t PIR; /*!< OCTOSPI Polling Interval register, Address offset: 0x090 */ + uint32_t RESERVED8[27]; /*!< Reserved, Address offset: 0x094-0x0FC */ + __IO uint32_t CCR; /*!< OCTOSPI Communication Configuration register, Address offset: 0x100 */ + uint32_t RESERVED9; /*!< Reserved, Address offset: 0x104 */ + __IO uint32_t TCR; /*!< OCTOSPI Timing Configuration register, Address offset: 0x108 */ + uint32_t RESERVED10; /*!< Reserved, Address offset: 0x10C */ + __IO uint32_t IR; /*!< OCTOSPI Instruction register, Address offset: 0x110 */ + uint32_t RESERVED11[3]; /*!< Reserved, Address offset: 0x114-0x11C */ + __IO uint32_t ABR; /*!< OCTOSPI Alternate Bytes register, Address offset: 0x120 */ + uint32_t RESERVED12[3]; /*!< Reserved, Address offset: 0x124-0x12C */ + __IO uint32_t LPTR; /*!< OCTOSPI Low Power Timeout register, Address offset: 0x130 */ + uint32_t RESERVED13[3]; /*!< Reserved, Address offset: 0x134-0x13C */ + __IO uint32_t WPCCR; /*!< OCTOSPI Wrap Communication Configuration register, Address offset: 0x140 */ + uint32_t RESERVED14; /*!< Reserved, Address offset: 0x144 */ + __IO uint32_t WPTCR; /*!< OCTOSPI Wrap Timing Configuration register, Address offset: 0x148 */ + uint32_t RESERVED15; /*!< Reserved, Address offset: 0x14C */ + __IO uint32_t WPIR; /*!< OCTOSPI Wrap Instruction register, Address offset: 0x150 */ + uint32_t RESERVED16[3]; /*!< Reserved, Address offset: 0x154-0x15C */ + __IO uint32_t WPABR; /*!< OCTOSPI Wrap Alternate Bytes register, Address offset: 0x160 */ + uint32_t RESERVED17[7]; /*!< Reserved, Address offset: 0x164-0x17C */ + __IO uint32_t WCCR; /*!< OCTOSPI Write Communication Configuration register, Address offset: 0x180 */ + uint32_t RESERVED18; /*!< Reserved, Address offset: 0x184 */ + __IO uint32_t WTCR; /*!< OCTOSPI Write Timing Configuration register, Address offset: 0x188 */ + uint32_t RESERVED19; /*!< Reserved, Address offset: 0x18C */ + __IO uint32_t WIR; /*!< OCTOSPI Write Instruction register, Address offset: 0x190 */ + uint32_t RESERVED20[3]; /*!< Reserved, Address offset: 0x194-0x19C */ + __IO uint32_t WABR; /*!< OCTOSPI Write Alternate Bytes register, Address offset: 0x1A0 */ + uint32_t RESERVED21[23]; /*!< Reserved, Address offset: 0x1A4-0x1FC */ + __IO uint32_t HLCR; /*!< OCTOSPI Hyperbus Latency Configuration register, Address offset: 0x200 */ + uint32_t RESERVED22[122]; /*!< Reserved, Address offset: 0x204-0x3EC */ + __IO uint32_t HWCFGR; /*!< OCTOSPI HW Configuration register, Address offset: 0x3F0 */ + __IO uint32_t VER; /*!< OCTOSPI Version register, Address offset: 0x3F4 */ + __IO uint32_t ID; /*!< OCTOSPI Identification register, Address offset: 0x3F8 */ + __IO uint32_t MID; /*!< OCTOPSI HW Magic ID register, Address offset: 0x3FC */ +} OCTOSPI_TypeDef; + +/** + * @} + */ +/** + * @brief OCTO Serial Peripheral Interface IO Manager + */ + +typedef struct +{ + __IO uint32_t CR; /*!< OCTOSPI IO Manager Control register, Address offset: 0x00 */ + __IO uint32_t PCR[3]; /*!< OCTOSPI IO Manager Port[1:3] Configuration register, Address offset: 0x04-0x20 */ +} OCTOSPIM_TypeDef; + +/** + * @} + */ + +/** + * @brief OTFD register + */ +typedef struct +{ + __IO uint32_t REG_CONFIGR; + __IO uint32_t REG_START_ADDR; + __IO uint32_t REG_END_ADDR; + __IO uint32_t REG_NONCER0; + __IO uint32_t REG_NONCER1; + __IO uint32_t REG_KEYR0; + __IO uint32_t REG_KEYR1; + __IO uint32_t REG_KEYR2; + __IO uint32_t REG_KEYR3; +} OTFDEC_Region_TypeDef; + +typedef struct +{ + __IO uint32_t CR; + uint32_t RESERVED1[191]; + __IO uint32_t ISR; + __IO uint32_t ICR; + __IO uint32_t IER; + uint32_t RESERVED2[56]; + __IO uint32_t HWCFGR2; + __IO uint32_t HWCFGR1; + __IO uint32_t VERR; + __IO uint32_t IPIDR; + __IO uint32_t SIDR; +} OTFDEC_TypeDef; +/** + * @} + */ + +/** + * @brief Global Programmer View + */ + +typedef struct +{ + uint32_t RESERVED0[2036]; /*!< Reserved, Address offset: 0x00-0x1FCC */ + __IO uint32_t AXI_PERIPH_ID_4; /*!< AXI interconnect - peripheral ID4 register, Address offset: 0x1FD0 */ + uint32_t AXI_PERIPH_ID_5; /*!< Reserved, Address offset: 0x1FD4 */ + uint32_t AXI_PERIPH_ID_6; /*!< Reserved, Address offset: 0x1FD8 */ + uint32_t AXI_PERIPH_ID_7; /*!< Reserved, Address offset: 0x1FDC */ + __IO uint32_t AXI_PERIPH_ID_0; /*!< AXI interconnect - peripheral ID0 register, Address offset: 0x1FE0 */ + __IO uint32_t AXI_PERIPH_ID_1; /*!< AXI interconnect - peripheral ID1 register, Address offset: 0x1FE4 */ + __IO uint32_t AXI_PERIPH_ID_2; /*!< AXI interconnect - peripheral ID2 register, Address offset: 0x1FE8 */ + __IO uint32_t AXI_PERIPH_ID_3; /*!< AXI interconnect - peripheral ID3 register, Address offset: 0x1FEC */ + __IO uint32_t AXI_COMP_ID_0; /*!< AXI interconnect - component ID0 register, Address offset: 0x1FF0 */ + __IO uint32_t AXI_COMP_ID_1; /*!< AXI interconnect - component ID1 register, Address offset: 0x1FF4 */ + __IO uint32_t AXI_COMP_ID_2; /*!< AXI interconnect - component ID2 register, Address offset: 0x1FF8 */ + __IO uint32_t AXI_COMP_ID_3; /*!< AXI interconnect - component ID3 register, Address offset: 0x1FFC */ + uint32_t RESERVED1[2]; /*!< Reserved, Address offset: 0x2000-0x2004 */ + __IO uint32_t AXI_TARG1_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 1 bus matrix issuing functionality register, Address offset: 0x2008 */ + uint32_t RESERVED2[6]; /*!< Reserved, Address offset: 0x200C-0x2020 */ + __IO uint32_t AXI_TARG1_FN_MOD2; /*!< AXI interconnect - TARG 1 bus matrix functionality 2 register, Address offset: 0x2024 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x2028 */ + __IO uint32_t AXI_TARG1_FN_MOD_LB; /*!< AXI interconnect - TARG 1 long burst functionality modification register, Address offset: 0x202C */ + uint32_t RESERVED4[54]; /*!< Reserved, Address offset: 0x2030-0x2104 */ + __IO uint32_t AXI_TARG1_FN_MOD; /*!< AXI interconnect - TARG 1 issuing functionality modification register, Address offset: 0x2108 */ + uint32_t RESERVED5[959]; /*!< Reserved, Address offset: 0x210C-0x3004 */ + __IO uint32_t AXI_TARG2_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 2 bus matrix issuing functionality register, Address offset: 0x3008 */ + uint32_t RESERVED6[6]; /*!< Reserved, Address offset: 0x300C-0x3020 */ + __IO uint32_t AXI_TARG2_FN_MOD2; /*!< AXI interconnect - TARG 2 bus matrix functionality 2 register, Address offset: 0x3024 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x3028 */ + __IO uint32_t AXI_TARG2_FN_MOD_LB; /*!< AXI interconnect - TARG 2 long burst functionality modification register, Address offset: 0x302C */ + uint32_t RESERVED8[54]; /*!< Reserved, Address offset: 0x3030-0x3104 */ + __IO uint32_t AXI_TARG2_FN_MOD; /*!< AXI interconnect - TARG 2 issuing functionality modification register, Address offset: 0x3108 */ + uint32_t RESERVED9[959]; /*!< Reserved, Address offset: 0x310C-0x4004 */ + __IO uint32_t AXI_TARG3_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 3 bus matrix issuing functionality register, Address offset: 0x4008 */ + uint32_t RESERVED10[1023]; /*!< Reserved, Address offset: 0x400C-0x5004 */ + __IO uint32_t AXI_TARG4_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 4 bus matrix issuing functionality register, Address offset: 0x5008 */ + uint32_t RESERVED11[1023]; /*!< Reserved, Address offset: 0x500C-0x6004 */ + __IO uint32_t AXI_TARG5_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 5 bus matrix issuing functionality register, Address offset: 0x6008 */ + uint32_t RESERVED12[1023]; /*!< Reserved, Address offset: 0x600C-0x7004 */ + __IO uint32_t AXI_TARG6_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 6 bus matrix issuing functionality register, Address offset: 0x7008 */ + uint32_t RESERVED13[1023]; /*!< Reserved, Address offset: 0x700C-0x8004 */ + __IO uint32_t AXI_TARG7_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 7 bus matrix issuing functionality register, Address offset: 0x8008 */ + uint32_t RESERVED14[6]; /*!< Reserved, Address offset: 0x800C-0x8020 */ + __IO uint32_t AXI_TARG7_FN_MOD2; /*!< AXI interconnect - TARG 7 bus matrix functionality 2 register, Address offset: 0x8024 */ + uint32_t RESERVED15; /*!< Reserved, Address offset: 0x8028 */ + __IO uint32_t AXI_TARG7_FN_MOD_LB; /*!< AXI interconnect - TARG 7 long burst functionality modification register, Address offset: 0x802C */ + uint32_t RESERVED16[54]; /*!< Reserved, Address offset: 0x8030-0x8104 */ + __IO uint32_t AXI_TARG7_FN_MOD; /*!< AXI interconnect - TARG 7 issuing functionality modification register, Address offset: 0x8108 */ + uint32_t RESERVED17[959]; /*!< Reserved, Address offset: 0x810C-0x9004 */ + __IO uint32_t AXI_TARG8_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 8 bus matrix issuing functionality register, Address offset: 0x9008 */ + uint32_t RESERVED117[6]; /*!< Reserved, Address offset: 0x900C-0x9020 */ + __IO uint32_t AXI_TARG8_FN_MOD2; /*!< AXI interconnect - TARG 8 bus matrix functionality 2 register, Address offset: 0x9024 */ + uint32_t RESERVED118[56]; /*!< Reserved, Address offset: 0x9028-0x9104 */ + __IO uint32_t AXI_TARG8_FN_MOD; /*!< AXI interconnect - TARG 8 issuing functionality modification register, Address offset: 0x9108 */ + uint32_t RESERVED119[58310]; /*!< Reserved, Address offset: 0x910C-0x42020 */ + __IO uint32_t AXI_INI1_FN_MOD2; /*!< AXI interconnect - INI 1 functionality modification 2 register, Address offset: 0x42024 */ + __IO uint32_t AXI_INI1_FN_MOD_AHB; /*!< AXI interconnect - INI 1 AHB functionality modification register, Address offset: 0x42028 */ + uint32_t RESERVED18[53]; /*!< Reserved, Address offset: 0x4202C-0x420FC */ + __IO uint32_t AXI_INI1_READ_QOS; /*!< AXI interconnect - INI 1 read QoS register, Address offset: 0x42100 */ + __IO uint32_t AXI_INI1_WRITE_QOS; /*!< AXI interconnect - INI 1 write QoS register, Address offset: 0x42104 */ + __IO uint32_t AXI_INI1_FN_MOD; /*!< AXI interconnect - INI 1 issuing functionality modification register, Address offset: 0x42108 */ + uint32_t RESERVED19[1021]; /*!< Reserved, Address offset: 0x4210C-0x430FC */ + __IO uint32_t AXI_INI2_READ_QOS; /*!< AXI interconnect - INI 2 read QoS register, Address offset: 0x43100 */ + __IO uint32_t AXI_INI2_WRITE_QOS; /*!< AXI interconnect - INI 2 write QoS register, Address offset: 0x43104 */ + __IO uint32_t AXI_INI2_FN_MOD; /*!< AXI interconnect - INI 2 issuing functionality modification register, Address offset: 0x43108 */ + uint32_t RESERVED20[966]; /*!< Reserved, Address offset: 0x4310C-0x44020 */ + __IO uint32_t AXI_INI3_FN_MOD2; /*!< AXI interconnect - INI 3 functionality modification 2 register, Address offset: 0x44024 */ + __IO uint32_t AXI_INI3_FN_MOD_AHB; /*!< AXI interconnect - INI 3 AHB functionality modification register, Address offset: 0x44028 */ + uint32_t RESERVED21[53]; /*!< Reserved, Address offset: 0x4402C-0x440FC */ + __IO uint32_t AXI_INI3_READ_QOS; /*!< AXI interconnect - INI 3 read QoS register, Address offset: 0x44100 */ + __IO uint32_t AXI_INI3_WRITE_QOS; /*!< AXI interconnect - INI 3 write QoS register, Address offset: 0x44104 */ + __IO uint32_t AXI_INI3_FN_MOD; /*!< AXI interconnect - INI 3 issuing functionality modification register, Address offset: 0x44108 */ + uint32_t RESERVED22[1021]; /*!< Reserved, Address offset: 0x4410C-0x450FC */ + __IO uint32_t AXI_INI4_READ_QOS; /*!< AXI interconnect - INI 4 read QoS register, Address offset: 0x45100 */ + __IO uint32_t AXI_INI4_WRITE_QOS; /*!< AXI interconnect - INI 4 write QoS register, Address offset: 0x45104 */ + __IO uint32_t AXI_INI4_FN_MOD; /*!< AXI interconnect - INI 4 issuing functionality modification register, Address offset: 0x45108 */ + uint32_t RESERVED23[1021]; /*!< Reserved, Address offset: 0x4510C-0x460FC */ + __IO uint32_t AXI_INI5_READ_QOS; /*!< AXI interconnect - INI 5 read QoS register, Address offset: 0x46100 */ + __IO uint32_t AXI_INI5_WRITE_QOS; /*!< AXI interconnect - INI 5 write QoS register, Address offset: 0x46104 */ + __IO uint32_t AXI_INI5_FN_MOD; /*!< AXI interconnect - INI 5 issuing functionality modification register, Address offset: 0x46108 */ + uint32_t RESERVED24[1021]; /*!< Reserved, Address offset: 0x4610C-0x470FC */ + __IO uint32_t AXI_INI6_READ_QOS; /*!< AXI interconnect - INI 6 read QoS register, Address offset: 0x47100 */ + __IO uint32_t AXI_INI6_WRITE_QOS; /*!< AXI interconnect - INI 6 write QoS register, Address offset: 0x47104 */ + __IO uint32_t AXI_INI6_FN_MOD; /*!< AXI interconnect - INI 6 issuing functionality modification register, Address offset: 0x47108 */ + +} GPV_TypeDef; + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define D1_ITCMRAM_BASE (0x00000000UL) /*!< Base address of : 64KB RAM reserved for CPU execution/instruction accessible over ITCM */ +#define D1_ITCMICP_BASE (0x00100000UL) /*!< Base address of : (up to 128KB) embedded Test FLASH memory accessible over ITCM */ +#define D1_DTCMRAM_BASE (0x20000000UL) /*!< Base address of : 128KB system data RAM accessible over DTCM */ +#define D1_AXIFLASH_BASE (0x08000000UL) /*!< Base address of : (up to 1 MB) embedded FLASH memory accessible over AXI */ +#define D1_AXIICP_BASE (0x1FF00000UL) /*!< Base address of : (up to 128KB) embedded Test FLASH memory accessible over AXI */ +#define D1_AXISRAM1_BASE (0x24000000UL) /*!< Base address of : (up to 128KB) system data RAM1 accessible over over AXI */ +#define D1_AXISRAM2_BASE (0x24020000UL) /*!< Base address of : (up to 192KB) system data RAM2 accessible over over AXI to be shared with ITCM (64K granularity) */ +#define D1_AXISRAM_BASE D1_AXISRAM1_BASE /*!< Base address of : (up to 320KB) system data RAM1/2 accessible over over AXI */ + +#define D2_AHBSRAM1_BASE (0x30000000UL) /*!< Base address of : (up to 16KB) system data RAM accessible over over AXI->AHB Bridge */ +#define D2_AHBSRAM2_BASE (0x30004000UL) /*!< Base address of : (up to 16KB) system data RAM accessible over over AXI->AHB Bridge */ +#define D2_AHBSRAM_BASE D2_AHBSRAM1_BASE /*!< Base address of : (up to 32KB) system data RAM1/2 accessible over over AXI->AHB Bridge */ + +#define D3_BKPSRAM_BASE (0x38800000UL) /*!< Base address of : Backup SRAM(4 KB) over AXI->AHB Bridge */ +#define D3_SRAM_BASE (0x38000000UL) /*!< Base address of : Backup SRAM(16 KB) over AXI->AHB Bridge */ + +#define PERIPH_BASE (0x40000000UL) /*!< Base address of : AHB/APB Peripherals */ +#define OCTOSPI1_BASE (0x90000000UL) /*!< Base address of : OCTOSPI1 memories accessible over AXI */ +#define OCTOSPI2_BASE (0x70000000UL) /*!< Base address of : OCTOSPI2 memories accessible over AXI */ + +#define FLASH_BANK1_BASE (0x08000000UL) /*!< Base address of : (up to 1 MB) Flash Bank1 accessible over AXI */ +#define FLASH_END (0x080FFFFFUL) /*!< FLASH end address */ + + +/* Legacy define */ +#define FLASH_BASE FLASH_BANK1_BASE + +/*!< Device electronic signature memory map */ +#define UID_BASE (0x1FF1E800UL) /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE (0x1FF1E880UL) /*!< FLASH Size register base address */ + + +/*!< Peripheral memory map */ +#define D2_APB1PERIPH_BASE PERIPH_BASE +#define D2_APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define D2_AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define D2_AHB2PERIPH_BASE (PERIPH_BASE + 0x08020000UL) + +#define D1_APB1PERIPH_BASE (PERIPH_BASE + 0x10000000UL) +#define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000UL) + +#define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL) +#define D3_AHB1PERIPH_BASE (PERIPH_BASE + 0x18020000UL) + +/*!< Legacy Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL) + + +/*!< D1_AHB1PERIPH peripherals */ + +#define MDMA_BASE (D1_AHB1PERIPH_BASE + 0x0000UL) +#define DMA2D_BASE (D1_AHB1PERIPH_BASE + 0x1000UL) +#define FLASH_R_BASE (D1_AHB1PERIPH_BASE + 0x2000UL) +#define FMC_R_BASE (D1_AHB1PERIPH_BASE + 0x4000UL) +#define OCTOSPI1_R_BASE (D1_AHB1PERIPH_BASE + 0x5000UL) +#define DLYB_OCTOSPI1_BASE (D1_AHB1PERIPH_BASE + 0x6000UL) +#define SDMMC1_BASE (D1_AHB1PERIPH_BASE + 0x7000UL) +#define DLYB_SDMMC1_BASE (D1_AHB1PERIPH_BASE + 0x8000UL) +#define RAMECC1_BASE (D1_AHB1PERIPH_BASE + 0x9000UL) +#define OCTOSPI2_R_BASE (D1_AHB1PERIPH_BASE + 0xA000UL) +#define DLYB_OCTOSPI2_BASE (D1_AHB1PERIPH_BASE + 0xB000UL) +#define OCTOSPIM_BASE (D1_AHB1PERIPH_BASE + 0xB400UL) + +#define OTFDEC1_BASE (D1_AHB1PERIPH_BASE + 0xB800UL) +#define OTFDEC1_REGION1_BASE (OTFDEC1_BASE + 0x20UL) +#define OTFDEC1_REGION2_BASE (OTFDEC1_BASE + 0x50UL) +#define OTFDEC1_REGION3_BASE (OTFDEC1_BASE + 0x80UL) +#define OTFDEC1_REGION4_BASE (OTFDEC1_BASE + 0xB0UL) +#define OTFDEC2_BASE (D1_AHB1PERIPH_BASE + 0xBC00UL) +#define OTFDEC2_REGION1_BASE (OTFDEC2_BASE + 0x20UL) +#define OTFDEC2_REGION2_BASE (OTFDEC2_BASE + 0x50UL) +#define OTFDEC2_REGION3_BASE (OTFDEC2_BASE + 0x80UL) +#define OTFDEC2_REGION4_BASE (OTFDEC2_BASE + 0xB0UL) + +/*!< D2_AHB1PERIPH peripherals */ + +#define DMA1_BASE (D2_AHB1PERIPH_BASE + 0x0000UL) +#define DMA2_BASE (D2_AHB1PERIPH_BASE + 0x0400UL) +#define DMAMUX1_BASE (D2_AHB1PERIPH_BASE + 0x0800UL) +#define ADC1_BASE (D2_AHB1PERIPH_BASE + 0x2000UL) +#define ADC2_BASE (D2_AHB1PERIPH_BASE + 0x2100UL) +#define ADC12_COMMON_BASE (D2_AHB1PERIPH_BASE + 0x2300UL) +#define ETH_BASE (D2_AHB1PERIPH_BASE + 0x8000UL) +#define ETH_MAC_BASE (ETH_BASE) + +/*!< USB registers base address */ +#define USB1_OTG_HS_PERIPH_BASE (0x40040000UL) +#define USB_OTG_GLOBAL_BASE (0x000UL) +#define USB_OTG_DEVICE_BASE (0x800UL) +#define USB_OTG_IN_ENDPOINT_BASE (0x900UL) +#define USB_OTG_OUT_ENDPOINT_BASE (0xB00UL) +#define USB_OTG_EP_REG_SIZE (0x20UL) +#define USB_OTG_HOST_BASE (0x400UL) +#define USB_OTG_HOST_PORT_BASE (0x440UL) +#define USB_OTG_HOST_CHANNEL_BASE (0x500UL) +#define USB_OTG_HOST_CHANNEL_SIZE (0x20UL) +#define USB_OTG_PCGCCTL_BASE (0xE00UL) +#define USB_OTG_FIFO_BASE (0x1000UL) +#define USB_OTG_FIFO_SIZE (0x1000UL) + +/*!< D2_AHB2PERIPH peripherals */ + +#define DCMI_BASE (D2_AHB2PERIPH_BASE + 0x0000UL) +#define PSSI_BASE (D2_AHB2PERIPH_BASE + 0x0400UL) +#define CRYP_BASE (D2_AHB2PERIPH_BASE + 0x1000UL) +#define HASH_BASE (D2_AHB2PERIPH_BASE + 0x1400UL) +#define HASH_DIGEST_BASE (D2_AHB2PERIPH_BASE + 0x1710UL) +#define RNG_BASE (D2_AHB2PERIPH_BASE + 0x1800UL) +#define SDMMC2_BASE (D2_AHB2PERIPH_BASE + 0x2400UL) +#define DLYB_SDMMC2_BASE (D2_AHB2PERIPH_BASE + 0x2800UL) +#define RAMECC2_BASE (D2_AHB2PERIPH_BASE + 0x3000UL) +#define FMAC_BASE (D2_AHB2PERIPH_BASE + 0x4000UL) +#define CORDIC_BASE (D2_AHB2PERIPH_BASE + 0x4400UL) + +/*!< D3_AHB1PERIPH peripherals */ +#define GPIOA_BASE (D3_AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (D3_AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (D3_AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (D3_AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (D3_AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (D3_AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (D3_AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (D3_AHB1PERIPH_BASE + 0x1C00UL) +#define GPIOJ_BASE (D3_AHB1PERIPH_BASE + 0x2400UL) +#define GPIOK_BASE (D3_AHB1PERIPH_BASE + 0x2800UL) +#define RCC_BASE (D3_AHB1PERIPH_BASE + 0x4400UL) +#define PWR_BASE (D3_AHB1PERIPH_BASE + 0x4800UL) +#define CRC_BASE (D3_AHB1PERIPH_BASE + 0x4C00UL) +#define BDMA_BASE (D3_AHB1PERIPH_BASE + 0x5400UL) +#define DMAMUX2_BASE (D3_AHB1PERIPH_BASE + 0x5800UL) +#define ADC3_BASE (D3_AHB1PERIPH_BASE + 0x6000UL) +#define ADC3_COMMON_BASE (D3_AHB1PERIPH_BASE + 0x6300UL) +#define HSEM_BASE (D3_AHB1PERIPH_BASE + 0x6400UL) +#define RAMECC3_BASE (D3_AHB1PERIPH_BASE + 0x7000UL) + +/*!< D1_APB1PERIPH peripherals */ +#define LTDC_BASE (D1_APB1PERIPH_BASE + 0x1000UL) +#define LTDC_Layer1_BASE (LTDC_BASE + 0x84UL) +#define LTDC_Layer2_BASE (LTDC_BASE + 0x104UL) +#define WWDG1_BASE (D1_APB1PERIPH_BASE + 0x3000UL) + +/*!< D2_APB1PERIPH peripherals */ +#define TIM2_BASE (D2_APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (D2_APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (D2_APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (D2_APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (D2_APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (D2_APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (D2_APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (D2_APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (D2_APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (D2_APB1PERIPH_BASE + 0x2400UL) + + +#define SPI2_BASE (D2_APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (D2_APB1PERIPH_BASE + 0x3C00UL) +#define SPDIFRX_BASE (D2_APB1PERIPH_BASE + 0x4000UL) +#define USART2_BASE (D2_APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (D2_APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (D2_APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (D2_APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (D2_APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (D2_APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (D2_APB1PERIPH_BASE + 0x5C00UL) +#define I2C5_BASE (D2_APB1PERIPH_BASE + 0x6400UL) +#define CEC_BASE (D2_APB1PERIPH_BASE + 0x6C00UL) +#define DAC1_BASE (D2_APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (D2_APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (D2_APB1PERIPH_BASE + 0x7C00UL) +#define CRS_BASE (D2_APB1PERIPH_BASE + 0x8400UL) +#define SWPMI1_BASE (D2_APB1PERIPH_BASE + 0x8800UL) +#define OPAMP_BASE (D2_APB1PERIPH_BASE + 0x9000UL) +#define OPAMP1_BASE (D2_APB1PERIPH_BASE + 0x9000UL) +#define OPAMP2_BASE (D2_APB1PERIPH_BASE + 0x9010UL) +#define MDIOS_BASE (D2_APB1PERIPH_BASE + 0x9400UL) +#define FDCAN1_BASE (D2_APB1PERIPH_BASE + 0xA000UL) +#define FDCAN2_BASE (D2_APB1PERIPH_BASE + 0xA400UL) +#define FDCAN_CCU_BASE (D2_APB1PERIPH_BASE + 0xA800UL) +#define SRAMCAN_BASE (D2_APB1PERIPH_BASE + 0xAC00UL) +#define FDCAN3_BASE (D2_APB1PERIPH_BASE + 0xD400UL) +#define TIM23_BASE (D2_APB1PERIPH_BASE + 0xE000UL) +#define TIM24_BASE (D2_APB1PERIPH_BASE + 0xE400UL) + +/*!< D2_APB2PERIPH peripherals */ + +#define TIM1_BASE (D2_APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (D2_APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (D2_APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (D2_APB2PERIPH_BASE + 0x1400UL) +#define UART9_BASE (D2_APB2PERIPH_BASE + 0x1800UL) +#define USART10_BASE (D2_APB2PERIPH_BASE + 0x1C00UL) +#define SPI1_BASE (D2_APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (D2_APB2PERIPH_BASE + 0x3400UL) +#define TIM15_BASE (D2_APB2PERIPH_BASE + 0x4000UL) +#define TIM16_BASE (D2_APB2PERIPH_BASE + 0x4400UL) +#define TIM17_BASE (D2_APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (D2_APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (D2_APB2PERIPH_BASE + 0x5800UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) +#define DFSDM1_BASE (D2_APB2PERIPH_BASE + 0x7800UL) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00UL) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20UL) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40UL) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60UL) +#define DFSDM1_Channel4_BASE (DFSDM1_BASE + 0x80UL) +#define DFSDM1_Channel5_BASE (DFSDM1_BASE + 0xA0UL) +#define DFSDM1_Channel6_BASE (DFSDM1_BASE + 0xC0UL) +#define DFSDM1_Channel7_BASE (DFSDM1_BASE + 0xE0UL) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100UL) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180UL) +#define DFSDM1_Filter2_BASE (DFSDM1_BASE + 0x200UL) +#define DFSDM1_Filter3_BASE (DFSDM1_BASE + 0x280UL) + + +/*!< D3_APB1PERIPH peripherals */ +#define EXTI_BASE (D3_APB1PERIPH_BASE + 0x0000UL) +#define EXTI_D1_BASE (EXTI_BASE + 0x0080UL) +#define EXTI_D2_BASE (EXTI_BASE + 0x00C0UL) +#define SYSCFG_BASE (D3_APB1PERIPH_BASE + 0x0400UL) +#define LPUART1_BASE (D3_APB1PERIPH_BASE + 0x0C00UL) +#define SPI6_BASE (D3_APB1PERIPH_BASE + 0x1400UL) +#define I2C4_BASE (D3_APB1PERIPH_BASE + 0x1C00UL) +#define LPTIM2_BASE (D3_APB1PERIPH_BASE + 0x2400UL) +#define LPTIM3_BASE (D3_APB1PERIPH_BASE + 0x2800UL) +#define LPTIM4_BASE (D3_APB1PERIPH_BASE + 0x2C00UL) +#define LPTIM5_BASE (D3_APB1PERIPH_BASE + 0x3000UL) +#define COMP12_BASE (D3_APB1PERIPH_BASE + 0x3800UL) +#define COMP1_BASE (COMP12_BASE + 0x0CUL) +#define COMP2_BASE (COMP12_BASE + 0x10UL) +#define VREFBUF_BASE (D3_APB1PERIPH_BASE + 0x3C00UL) +#define RTC_BASE (D3_APB1PERIPH_BASE + 0x4000UL) +#define IWDG1_BASE (D3_APB1PERIPH_BASE + 0x4800UL) + + +#define SAI4_BASE (D3_APB1PERIPH_BASE + 0x5400UL) +#define SAI4_Block_A_BASE (SAI4_BASE + 0x004UL) +#define SAI4_Block_B_BASE (SAI4_BASE + 0x024UL) + +#define DTS_BASE (D3_APB1PERIPH_BASE + 0x6800UL) + + + +#define BDMA_Channel0_BASE (BDMA_BASE + 0x0008UL) +#define BDMA_Channel1_BASE (BDMA_BASE + 0x001CUL) +#define BDMA_Channel2_BASE (BDMA_BASE + 0x0030UL) +#define BDMA_Channel3_BASE (BDMA_BASE + 0x0044UL) +#define BDMA_Channel4_BASE (BDMA_BASE + 0x0058UL) +#define BDMA_Channel5_BASE (BDMA_BASE + 0x006CUL) +#define BDMA_Channel6_BASE (BDMA_BASE + 0x0080UL) +#define BDMA_Channel7_BASE (BDMA_BASE + 0x0094UL) + +#define DMAMUX2_Channel0_BASE (DMAMUX2_BASE) +#define DMAMUX2_Channel1_BASE (DMAMUX2_BASE + 0x0004UL) +#define DMAMUX2_Channel2_BASE (DMAMUX2_BASE + 0x0008UL) +#define DMAMUX2_Channel3_BASE (DMAMUX2_BASE + 0x000CUL) +#define DMAMUX2_Channel4_BASE (DMAMUX2_BASE + 0x0010UL) +#define DMAMUX2_Channel5_BASE (DMAMUX2_BASE + 0x0014UL) +#define DMAMUX2_Channel6_BASE (DMAMUX2_BASE + 0x0018UL) +#define DMAMUX2_Channel7_BASE (DMAMUX2_BASE + 0x001CUL) + +#define DMAMUX2_RequestGenerator0_BASE (DMAMUX2_BASE + 0x0100UL) +#define DMAMUX2_RequestGenerator1_BASE (DMAMUX2_BASE + 0x0104UL) +#define DMAMUX2_RequestGenerator2_BASE (DMAMUX2_BASE + 0x0108UL) +#define DMAMUX2_RequestGenerator3_BASE (DMAMUX2_BASE + 0x010CUL) +#define DMAMUX2_RequestGenerator4_BASE (DMAMUX2_BASE + 0x0110UL) +#define DMAMUX2_RequestGenerator5_BASE (DMAMUX2_BASE + 0x0114UL) +#define DMAMUX2_RequestGenerator6_BASE (DMAMUX2_BASE + 0x0118UL) +#define DMAMUX2_RequestGenerator7_BASE (DMAMUX2_BASE + 0x011CUL) + +#define DMAMUX2_ChannelStatus_BASE (DMAMUX2_BASE + 0x0080UL) +#define DMAMUX2_RequestGenStatus_BASE (DMAMUX2_BASE + 0x0140UL) + +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) + +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) + +#define DMAMUX1_Channel0_BASE (DMAMUX1_BASE) +#define DMAMUX1_Channel1_BASE (DMAMUX1_BASE + 0x0004UL) +#define DMAMUX1_Channel2_BASE (DMAMUX1_BASE + 0x0008UL) +#define DMAMUX1_Channel3_BASE (DMAMUX1_BASE + 0x000CUL) +#define DMAMUX1_Channel4_BASE (DMAMUX1_BASE + 0x0010UL) +#define DMAMUX1_Channel5_BASE (DMAMUX1_BASE + 0x0014UL) +#define DMAMUX1_Channel6_BASE (DMAMUX1_BASE + 0x0018UL) +#define DMAMUX1_Channel7_BASE (DMAMUX1_BASE + 0x001CUL) +#define DMAMUX1_Channel8_BASE (DMAMUX1_BASE + 0x0020UL) +#define DMAMUX1_Channel9_BASE (DMAMUX1_BASE + 0x0024UL) +#define DMAMUX1_Channel10_BASE (DMAMUX1_BASE + 0x0028UL) +#define DMAMUX1_Channel11_BASE (DMAMUX1_BASE + 0x002CUL) +#define DMAMUX1_Channel12_BASE (DMAMUX1_BASE + 0x0030UL) +#define DMAMUX1_Channel13_BASE (DMAMUX1_BASE + 0x0034UL) +#define DMAMUX1_Channel14_BASE (DMAMUX1_BASE + 0x0038UL) +#define DMAMUX1_Channel15_BASE (DMAMUX1_BASE + 0x003CUL) + +#define DMAMUX1_RequestGenerator0_BASE (DMAMUX1_BASE + 0x0100UL) +#define DMAMUX1_RequestGenerator1_BASE (DMAMUX1_BASE + 0x0104UL) +#define DMAMUX1_RequestGenerator2_BASE (DMAMUX1_BASE + 0x0108UL) +#define DMAMUX1_RequestGenerator3_BASE (DMAMUX1_BASE + 0x010CUL) +#define DMAMUX1_RequestGenerator4_BASE (DMAMUX1_BASE + 0x0110UL) +#define DMAMUX1_RequestGenerator5_BASE (DMAMUX1_BASE + 0x0114UL) +#define DMAMUX1_RequestGenerator6_BASE (DMAMUX1_BASE + 0x0118UL) +#define DMAMUX1_RequestGenerator7_BASE (DMAMUX1_BASE + 0x011CUL) + +#define DMAMUX1_ChannelStatus_BASE (DMAMUX1_BASE + 0x0080UL) +#define DMAMUX1_RequestGenStatus_BASE (DMAMUX1_BASE + 0x0140UL) + +/*!< FMC Banks registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank2_R_BASE (FMC_R_BASE + 0x0060UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140UL) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE (0x5C001000UL) + +#define MDMA_Channel0_BASE (MDMA_BASE + 0x00000040UL) +#define MDMA_Channel1_BASE (MDMA_BASE + 0x00000080UL) +#define MDMA_Channel2_BASE (MDMA_BASE + 0x000000C0UL) +#define MDMA_Channel3_BASE (MDMA_BASE + 0x00000100UL) +#define MDMA_Channel4_BASE (MDMA_BASE + 0x00000140UL) +#define MDMA_Channel5_BASE (MDMA_BASE + 0x00000180UL) +#define MDMA_Channel6_BASE (MDMA_BASE + 0x000001C0UL) +#define MDMA_Channel7_BASE (MDMA_BASE + 0x00000200UL) +#define MDMA_Channel8_BASE (MDMA_BASE + 0x00000240UL) +#define MDMA_Channel9_BASE (MDMA_BASE + 0x00000280UL) +#define MDMA_Channel10_BASE (MDMA_BASE + 0x000002C0UL) +#define MDMA_Channel11_BASE (MDMA_BASE + 0x00000300UL) +#define MDMA_Channel12_BASE (MDMA_BASE + 0x00000340UL) +#define MDMA_Channel13_BASE (MDMA_BASE + 0x00000380UL) +#define MDMA_Channel14_BASE (MDMA_BASE + 0x000003C0UL) +#define MDMA_Channel15_BASE (MDMA_BASE + 0x00000400UL) + +#define RAMECC1_Monitor1_BASE (RAMECC1_BASE + 0x20UL) +#define RAMECC1_Monitor2_BASE (RAMECC1_BASE + 0x40UL) +#define RAMECC1_Monitor3_BASE (RAMECC1_BASE + 0x60UL) +#define RAMECC1_Monitor4_BASE (RAMECC1_BASE + 0x80UL) +#define RAMECC1_Monitor5_BASE (RAMECC1_BASE + 0xA0UL) +#define RAMECC1_Monitor6_BASE (RAMECC1_BASE + 0xC0UL) + +#define RAMECC2_Monitor1_BASE (RAMECC2_BASE + 0x20UL) +#define RAMECC2_Monitor2_BASE (RAMECC2_BASE + 0x40UL) +#define RAMECC2_Monitor3_BASE (RAMECC2_BASE + 0x60UL) + +#define RAMECC3_Monitor1_BASE (RAMECC3_BASE + 0x20UL) +#define RAMECC3_Monitor2_BASE (RAMECC3_BASE + 0x40UL) + + + +#define GPV_BASE (PERIPH_BASE + 0x11000000UL) /*!< GPV_BASE (PERIPH_BASE + 0x11000000UL) */ + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define VREFBUF ((VREFBUF_TypeDef *) VREFBUF_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG1 ((WWDG_TypeDef *) WWDG1_BASE) + + +#define IWDG1 ((IWDG_TypeDef *) IWDG1_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define SPI6 ((SPI_TypeDef *) SPI6_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define USART10 ((USART_TypeDef *) USART10_BASE) +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define CRS ((CRS_TypeDef *) CRS_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define I2C4 ((I2C_TypeDef *) I2C4_BASE) +#define I2C5 ((I2C_TypeDef *) I2C5_BASE) +#define FDCAN1 ((FDCAN_GlobalTypeDef *) FDCAN1_BASE) +#define FDCAN2 ((FDCAN_GlobalTypeDef *) FDCAN2_BASE) +#define FDCAN_CCU ((FDCAN_ClockCalibrationUnit_TypeDef *) FDCAN_CCU_BASE) +#define FDCAN3 ((FDCAN_GlobalTypeDef *) FDCAN3_BASE) +#define TIM23 ((TIM_TypeDef *) TIM23_BASE) +#define TIM24 ((TIM_TypeDef *) TIM24_BASE) +#define CEC ((CEC_TypeDef *) CEC_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC1_BASE) +#define LPUART1 ((USART_TypeDef *) LPUART1_BASE) +#define SWPMI1 ((SWPMI_TypeDef *) SWPMI1_BASE) +#define LPTIM2 ((LPTIM_TypeDef *) LPTIM2_BASE) +#define LPTIM3 ((LPTIM_TypeDef *) LPTIM3_BASE) +#define DTS ((DTS_TypeDef *) DTS_BASE) +#define LPTIM4 ((LPTIM_TypeDef *) LPTIM4_BASE) +#define LPTIM5 ((LPTIM_TypeDef *) LPTIM5_BASE) + +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define COMP12 ((COMPOPT_TypeDef *) COMP12_BASE) +#define COMP1 ((COMP_TypeDef *) COMP1_BASE) +#define COMP2 ((COMP_TypeDef *) COMP2_BASE) +#define COMP12_COMMON ((COMP_Common_TypeDef *) COMP2_BASE) +#define OPAMP ((OPAMP_TypeDef *) OPAMP_BASE) +#define OPAMP1 ((OPAMP_TypeDef *) OPAMP1_BASE) +#define OPAMP2 ((OPAMP_TypeDef *) OPAMP2_BASE) + + +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define EXTI_D1 ((EXTI_Core_TypeDef *) EXTI_D1_BASE) +#define EXTI_D2 ((EXTI_Core_TypeDef *) EXTI_D2_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM15 ((TIM_TypeDef *) TIM15_BASE) +#define TIM16 ((TIM_TypeDef *) TIM16_BASE) +#define TIM17 ((TIM_TypeDef *) TIM17_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define SAI4 ((SAI_TypeDef *) SAI4_BASE) +#define SAI4_Block_A ((SAI_Block_TypeDef *)SAI4_Block_A_BASE) +#define SAI4_Block_B ((SAI_Block_TypeDef *)SAI4_Block_B_BASE) + +#define SPDIFRX ((SPDIFRX_TypeDef *) SPDIFRX_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel4_BASE) +#define DFSDM1_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel5_BASE) +#define DFSDM1_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel6_BASE) +#define DFSDM1_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel7_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM1_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter2_BASE) +#define DFSDM1_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter3_BASE) +#define DMA2D ((DMA2D_TypeDef *) DMA2D_BASE) +#define DCMI ((DCMI_TypeDef *) DCMI_BASE) +#define PSSI ((PSSI_TypeDef *) PSSI_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) + +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) +#define GPIOK ((GPIO_TypeDef *) GPIOK_BASE) + +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC3_COMMON ((ADC_Common_TypeDef *) ADC3_COMMON_BASE) +#define ADC12_COMMON ((ADC_Common_TypeDef *) ADC12_COMMON_BASE) + +#define CRYP ((CRYP_TypeDef *) CRYP_BASE) +#define HASH ((HASH_TypeDef *) HASH_BASE) +#define HASH_DIGEST ((HASH_DIGEST_TypeDef *) HASH_DIGEST_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define SDMMC2 ((SDMMC_TypeDef *) SDMMC2_BASE) +#define DLYB_SDMMC2 ((DLYB_TypeDef *) DLYB_SDMMC2_BASE) +#define FMAC ((FMAC_TypeDef *) FMAC_BASE) +#define CORDIC ((CORDIC_TypeDef *) CORDIC_BASE) + +#define BDMA ((BDMA_TypeDef *) BDMA_BASE) +#define BDMA_Channel0 ((BDMA_Channel_TypeDef *) BDMA_Channel0_BASE) +#define BDMA_Channel1 ((BDMA_Channel_TypeDef *) BDMA_Channel1_BASE) +#define BDMA_Channel2 ((BDMA_Channel_TypeDef *) BDMA_Channel2_BASE) +#define BDMA_Channel3 ((BDMA_Channel_TypeDef *) BDMA_Channel3_BASE) +#define BDMA_Channel4 ((BDMA_Channel_TypeDef *) BDMA_Channel4_BASE) +#define BDMA_Channel5 ((BDMA_Channel_TypeDef *) BDMA_Channel5_BASE) +#define BDMA_Channel6 ((BDMA_Channel_TypeDef *) BDMA_Channel6_BASE) +#define BDMA_Channel7 ((BDMA_Channel_TypeDef *) BDMA_Channel7_BASE) + +#define RAMECC1 ((RAMECC_TypeDef *)RAMECC1_BASE) +#define RAMECC1_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor1_BASE) +#define RAMECC1_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor2_BASE) +#define RAMECC1_Monitor3 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor3_BASE) +#define RAMECC1_Monitor4 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor4_BASE) +#define RAMECC1_Monitor5 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor5_BASE) +#define RAMECC1_Monitor6 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor6_BASE) + +#define RAMECC2 ((RAMECC_TypeDef *)RAMECC2_BASE) +#define RAMECC2_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor1_BASE) +#define RAMECC2_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor2_BASE) +#define RAMECC2_Monitor3 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor3_BASE) + +#define RAMECC3 ((RAMECC_TypeDef *)RAMECC3_BASE) +#define RAMECC3_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC3_Monitor1_BASE) +#define RAMECC3_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC3_Monitor2_BASE) + +#define DMAMUX2 ((DMAMUX_Channel_TypeDef *) DMAMUX2_BASE) +#define DMAMUX2_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel0_BASE) +#define DMAMUX2_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel1_BASE) +#define DMAMUX2_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel2_BASE) +#define DMAMUX2_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel3_BASE) +#define DMAMUX2_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel4_BASE) +#define DMAMUX2_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel5_BASE) +#define DMAMUX2_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel6_BASE) +#define DMAMUX2_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel7_BASE) + + +#define DMAMUX2_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator0_BASE) +#define DMAMUX2_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator1_BASE) +#define DMAMUX2_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator2_BASE) +#define DMAMUX2_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator3_BASE) +#define DMAMUX2_RequestGenerator4 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator4_BASE) +#define DMAMUX2_RequestGenerator5 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator5_BASE) +#define DMAMUX2_RequestGenerator6 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator6_BASE) +#define DMAMUX2_RequestGenerator7 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator7_BASE) + +#define DMAMUX2_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX2_ChannelStatus_BASE) +#define DMAMUX2_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX2_RequestGenStatus_BASE) + +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) + +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) + + +#define DMAMUX1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_BASE) +#define DMAMUX1_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel0_BASE) +#define DMAMUX1_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel1_BASE) +#define DMAMUX1_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel2_BASE) +#define DMAMUX1_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel3_BASE) +#define DMAMUX1_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel4_BASE) +#define DMAMUX1_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel5_BASE) +#define DMAMUX1_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel6_BASE) +#define DMAMUX1_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel7_BASE) +#define DMAMUX1_Channel8 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel8_BASE) +#define DMAMUX1_Channel9 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel9_BASE) +#define DMAMUX1_Channel10 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel10_BASE) +#define DMAMUX1_Channel11 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel11_BASE) +#define DMAMUX1_Channel12 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel12_BASE) +#define DMAMUX1_Channel13 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel13_BASE) +#define DMAMUX1_Channel14 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel14_BASE) +#define DMAMUX1_Channel15 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel15_BASE) + +#define DMAMUX1_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator0_BASE) +#define DMAMUX1_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator1_BASE) +#define DMAMUX1_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator2_BASE) +#define DMAMUX1_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator3_BASE) +#define DMAMUX1_RequestGenerator4 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator4_BASE) +#define DMAMUX1_RequestGenerator5 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator5_BASE) +#define DMAMUX1_RequestGenerator6 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator6_BASE) +#define DMAMUX1_RequestGenerator7 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator7_BASE) + +#define DMAMUX1_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX1_ChannelStatus_BASE) +#define DMAMUX1_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX1_RequestGenStatus_BASE) + + +#define FMC_Bank1_R ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E_R ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank2_R ((FMC_Bank2_TypeDef *) FMC_Bank2_R_BASE) +#define FMC_Bank3_R ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) +#define FMC_Bank5_6_R ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) + +#define OCTOSPI1 ((OCTOSPI_TypeDef *) OCTOSPI1_R_BASE) +#define DLYB_OCTOSPI1 ((DLYB_TypeDef *) DLYB_OCTOSPI1_BASE) +#define OCTOSPI2 ((OCTOSPI_TypeDef *) OCTOSPI2_R_BASE) +#define DLYB_OCTOSPI2 ((DLYB_TypeDef *) DLYB_OCTOSPI2_BASE) +#define OCTOSPIM ((OCTOSPIM_TypeDef *) OCTOSPIM_BASE) + +#define OTFDEC1 ((OTFDEC_TypeDef *) OTFDEC1_BASE) +#define OTFDEC1_REGION1 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION1_BASE) +#define OTFDEC1_REGION2 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION2_BASE) +#define OTFDEC1_REGION3 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION3_BASE) +#define OTFDEC1_REGION4 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION4_BASE) + +#define OTFDEC2 ((OTFDEC_TypeDef *) OTFDEC2_BASE) +#define OTFDEC2_REGION1 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION1_BASE) +#define OTFDEC2_REGION2 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION2_BASE) +#define OTFDEC2_REGION3 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION3_BASE) +#define OTFDEC2_REGION4 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION4_BASE) + +#define SDMMC1 ((SDMMC_TypeDef *) SDMMC1_BASE) +#define DLYB_SDMMC1 ((DLYB_TypeDef *) DLYB_SDMMC1_BASE) + +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +#define HSEM ((HSEM_TypeDef *) HSEM_BASE) +#define HSEM_COMMON ((HSEM_Common_TypeDef *) (HSEM_BASE + 0x100UL)) + +#define LTDC ((LTDC_TypeDef *)LTDC_BASE) +#define LTDC_Layer1 ((LTDC_Layer_TypeDef *)LTDC_Layer1_BASE) +#define LTDC_Layer2 ((LTDC_Layer_TypeDef *)LTDC_Layer2_BASE) + +#define MDIOS ((MDIOS_TypeDef *) MDIOS_BASE) + +#define ETH ((ETH_TypeDef *)ETH_BASE) +#define MDMA ((MDMA_TypeDef *)MDMA_BASE) +#define MDMA_Channel0 ((MDMA_Channel_TypeDef *)MDMA_Channel0_BASE) +#define MDMA_Channel1 ((MDMA_Channel_TypeDef *)MDMA_Channel1_BASE) +#define MDMA_Channel2 ((MDMA_Channel_TypeDef *)MDMA_Channel2_BASE) +#define MDMA_Channel3 ((MDMA_Channel_TypeDef *)MDMA_Channel3_BASE) +#define MDMA_Channel4 ((MDMA_Channel_TypeDef *)MDMA_Channel4_BASE) +#define MDMA_Channel5 ((MDMA_Channel_TypeDef *)MDMA_Channel5_BASE) +#define MDMA_Channel6 ((MDMA_Channel_TypeDef *)MDMA_Channel6_BASE) +#define MDMA_Channel7 ((MDMA_Channel_TypeDef *)MDMA_Channel7_BASE) +#define MDMA_Channel8 ((MDMA_Channel_TypeDef *)MDMA_Channel8_BASE) +#define MDMA_Channel9 ((MDMA_Channel_TypeDef *)MDMA_Channel9_BASE) +#define MDMA_Channel10 ((MDMA_Channel_TypeDef *)MDMA_Channel10_BASE) +#define MDMA_Channel11 ((MDMA_Channel_TypeDef *)MDMA_Channel11_BASE) +#define MDMA_Channel12 ((MDMA_Channel_TypeDef *)MDMA_Channel12_BASE) +#define MDMA_Channel13 ((MDMA_Channel_TypeDef *)MDMA_Channel13_BASE) +#define MDMA_Channel14 ((MDMA_Channel_TypeDef *)MDMA_Channel14_BASE) +#define MDMA_Channel15 ((MDMA_Channel_TypeDef *)MDMA_Channel15_BASE) + + +#define USB1_OTG_HS ((USB_OTG_GlobalTypeDef *) USB1_OTG_HS_PERIPH_BASE) + +/* Legacy defines */ +#define USB_OTG_HS USB1_OTG_HS +#define USB_OTG_HS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE + +#define GPV ((GPV_TypeDef *) GPV_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +/******************************* ADC VERSION ********************************/ +#define ADC_VER_V5_V90 +/******************** Bit definition for ADC_ISR register ********************/ +#define ADC_ISR_ADRDY_Pos (0U) +#define ADC_ISR_ADRDY_Msk (0x1UL << ADC_ISR_ADRDY_Pos) /*!< 0x00000001 */ +#define ADC_ISR_ADRDY ADC_ISR_ADRDY_Msk /*!< ADC Ready (ADRDY) flag */ +#define ADC_ISR_EOSMP_Pos (1U) +#define ADC_ISR_EOSMP_Msk (0x1UL << ADC_ISR_EOSMP_Pos) /*!< 0x00000002 */ +#define ADC_ISR_EOSMP ADC_ISR_EOSMP_Msk /*!< ADC End of Sampling flag */ +#define ADC_ISR_EOC_Pos (2U) +#define ADC_ISR_EOC_Msk (0x1UL << ADC_ISR_EOC_Pos) /*!< 0x00000004 */ +#define ADC_ISR_EOC ADC_ISR_EOC_Msk /*!< ADC End of Regular Conversion flag */ +#define ADC_ISR_EOS_Pos (3U) +#define ADC_ISR_EOS_Msk (0x1UL << ADC_ISR_EOS_Pos) /*!< 0x00000008 */ +#define ADC_ISR_EOS ADC_ISR_EOS_Msk /*!< ADC End of Regular sequence of Conversions flag */ +#define ADC_ISR_OVR_Pos (4U) +#define ADC_ISR_OVR_Msk (0x1UL << ADC_ISR_OVR_Pos) /*!< 0x00000010 */ +#define ADC_ISR_OVR ADC_ISR_OVR_Msk /*!< ADC overrun flag */ +#define ADC_ISR_JEOC_Pos (5U) +#define ADC_ISR_JEOC_Msk (0x1UL << ADC_ISR_JEOC_Pos) /*!< 0x00000020 */ +#define ADC_ISR_JEOC ADC_ISR_JEOC_Msk /*!< ADC End of Injected Conversion flag */ +#define ADC_ISR_JEOS_Pos (6U) +#define ADC_ISR_JEOS_Msk (0x1UL << ADC_ISR_JEOS_Pos) /*!< 0x00000040 */ +#define ADC_ISR_JEOS ADC_ISR_JEOS_Msk /*!< ADC End of Injected sequence of Conversions flag */ +#define ADC_ISR_AWD1_Pos (7U) +#define ADC_ISR_AWD1_Msk (0x1UL << ADC_ISR_AWD1_Pos) /*!< 0x00000080 */ +#define ADC_ISR_AWD1 ADC_ISR_AWD1_Msk /*!< ADC Analog watchdog 1 flag */ +#define ADC_ISR_AWD2_Pos (8U) +#define ADC_ISR_AWD2_Msk (0x1UL << ADC_ISR_AWD2_Pos) /*!< 0x00000100 */ +#define ADC_ISR_AWD2 ADC_ISR_AWD2_Msk /*!< ADC Analog watchdog 2 flag */ +#define ADC_ISR_AWD3_Pos (9U) +#define ADC_ISR_AWD3_Msk (0x1UL << ADC_ISR_AWD3_Pos) /*!< 0x00000200 */ +#define ADC_ISR_AWD3 ADC_ISR_AWD3_Msk /*!< ADC Analog watchdog 3 flag */ +#define ADC_ISR_JQOVF_Pos (10U) +#define ADC_ISR_JQOVF_Msk (0x1UL << ADC_ISR_JQOVF_Pos) /*!< 0x00000400 */ +#define ADC_ISR_JQOVF ADC_ISR_JQOVF_Msk /*!< ADC Injected Context Queue Overflow flag */ +#define ADC_ISR_LDORDY_Pos (12U) +#define ADC_ISR_LDORDY_Msk (0x1UL << ADC_ISR_LDORDY_Pos) /*!< 0x00001000 */ +#define ADC_ISR_LDORDY ADC_ISR_LDORDY_Msk /*!< ADC LDO Ready (LDORDY) flag */ + +/******************** Bit definition for ADC_IER register ********************/ +#define ADC_IER_ADRDYIE_Pos (0U) +#define ADC_IER_ADRDYIE_Msk (0x1UL << ADC_IER_ADRDYIE_Pos) /*!< 0x00000001 */ +#define ADC_IER_ADRDYIE ADC_IER_ADRDYIE_Msk /*!< ADC Ready (ADRDY) interrupt source */ +#define ADC_IER_EOSMPIE_Pos (1U) +#define ADC_IER_EOSMPIE_Msk (0x1UL << ADC_IER_EOSMPIE_Pos) /*!< 0x00000002 */ +#define ADC_IER_EOSMPIE ADC_IER_EOSMPIE_Msk /*!< ADC End of Sampling interrupt source */ +#define ADC_IER_EOCIE_Pos (2U) +#define ADC_IER_EOCIE_Msk (0x1UL << ADC_IER_EOCIE_Pos) /*!< 0x00000004 */ +#define ADC_IER_EOCIE ADC_IER_EOCIE_Msk /*!< ADC End of Regular Conversion interrupt source */ +#define ADC_IER_EOSIE_Pos (3U) +#define ADC_IER_EOSIE_Msk (0x1UL << ADC_IER_EOSIE_Pos) /*!< 0x00000008 */ +#define ADC_IER_EOSIE ADC_IER_EOSIE_Msk /*!< ADC End of Regular sequence of Conversions interrupt source */ +#define ADC_IER_OVRIE_Pos (4U) +#define ADC_IER_OVRIE_Msk (0x1UL << ADC_IER_OVRIE_Pos) /*!< 0x00000010 */ +#define ADC_IER_OVRIE ADC_IER_OVRIE_Msk /*!< ADC overrun interrupt source */ +#define ADC_IER_JEOCIE_Pos (5U) +#define ADC_IER_JEOCIE_Msk (0x1UL << ADC_IER_JEOCIE_Pos) /*!< 0x00000020 */ +#define ADC_IER_JEOCIE ADC_IER_JEOCIE_Msk /*!< ADC End of Injected Conversion interrupt source */ +#define ADC_IER_JEOSIE_Pos (6U) +#define ADC_IER_JEOSIE_Msk (0x1UL << ADC_IER_JEOSIE_Pos) /*!< 0x00000040 */ +#define ADC_IER_JEOSIE ADC_IER_JEOSIE_Msk /*!< ADC End of Injected sequence of Conversions interrupt source */ +#define ADC_IER_AWD1IE_Pos (7U) +#define ADC_IER_AWD1IE_Msk (0x1UL << ADC_IER_AWD1IE_Pos) /*!< 0x00000080 */ +#define ADC_IER_AWD1IE ADC_IER_AWD1IE_Msk /*!< ADC Analog watchdog 1 interrupt source */ +#define ADC_IER_AWD2IE_Pos (8U) +#define ADC_IER_AWD2IE_Msk (0x1UL << ADC_IER_AWD2IE_Pos) /*!< 0x00000100 */ +#define ADC_IER_AWD2IE ADC_IER_AWD2IE_Msk /*!< ADC Analog watchdog 2 interrupt source */ +#define ADC_IER_AWD3IE_Pos (9U) +#define ADC_IER_AWD3IE_Msk (0x1UL << ADC_IER_AWD3IE_Pos) /*!< 0x00000200 */ +#define ADC_IER_AWD3IE ADC_IER_AWD3IE_Msk /*!< ADC Analog watchdog 3 interrupt source */ +#define ADC_IER_JQOVFIE_Pos (10U) +#define ADC_IER_JQOVFIE_Msk (0x1UL << ADC_IER_JQOVFIE_Pos) /*!< 0x00000400 */ +#define ADC_IER_JQOVFIE ADC_IER_JQOVFIE_Msk /*!< ADC Injected Context Queue Overflow interrupt source */ + +/******************** Bit definition for ADC_CR register ********************/ +#define ADC_CR_ADEN_Pos (0U) +#define ADC_CR_ADEN_Msk (0x1UL << ADC_CR_ADEN_Pos) /*!< 0x00000001 */ +#define ADC_CR_ADEN ADC_CR_ADEN_Msk /*!< ADC Enable control */ +#define ADC_CR_ADDIS_Pos (1U) +#define ADC_CR_ADDIS_Msk (0x1UL << ADC_CR_ADDIS_Pos) /*!< 0x00000002 */ +#define ADC_CR_ADDIS ADC_CR_ADDIS_Msk /*!< ADC Disable command */ +#define ADC_CR_ADSTART_Pos (2U) +#define ADC_CR_ADSTART_Msk (0x1UL << ADC_CR_ADSTART_Pos) /*!< 0x00000004 */ +#define ADC_CR_ADSTART ADC_CR_ADSTART_Msk /*!< ADC Start of Regular conversion */ +#define ADC_CR_JADSTART_Pos (3U) +#define ADC_CR_JADSTART_Msk (0x1UL << ADC_CR_JADSTART_Pos) /*!< 0x00000008 */ +#define ADC_CR_JADSTART ADC_CR_JADSTART_Msk /*!< ADC Start of injected conversion */ +#define ADC_CR_ADSTP_Pos (4U) +#define ADC_CR_ADSTP_Msk (0x1UL << ADC_CR_ADSTP_Pos) /*!< 0x00000010 */ +#define ADC_CR_ADSTP ADC_CR_ADSTP_Msk /*!< ADC Stop of Regular conversion */ +#define ADC_CR_JADSTP_Pos (5U) +#define ADC_CR_JADSTP_Msk (0x1UL << ADC_CR_JADSTP_Pos) /*!< 0x00000020 */ +#define ADC_CR_JADSTP ADC_CR_JADSTP_Msk /*!< ADC Stop of injected conversion */ +#define ADC_CR_BOOST_Pos (8U) +#define ADC_CR_BOOST_Msk (0x3UL << ADC_CR_BOOST_Pos) /*!< 0x00000300 */ +#define ADC_CR_BOOST ADC_CR_BOOST_Msk /*!< ADC Boost Mode configuration */ +#define ADC_CR_BOOST_0 (0x1UL << ADC_CR_BOOST_Pos) /*!< 0x00000100 */ +#define ADC_CR_BOOST_1 (0x2UL << ADC_CR_BOOST_Pos) /*!< 0x00000200 */ +#define ADC_CR_ADCALLIN_Pos (16U) +#define ADC_CR_ADCALLIN_Msk (0x1UL << ADC_CR_ADCALLIN_Pos) /*!< 0x00010000 */ +#define ADC_CR_ADCALLIN ADC_CR_ADCALLIN_Msk /*!< ADC Linearity calibration */ +#define ADC_CR_LINCALRDYW1_Pos (22U) +#define ADC_CR_LINCALRDYW1_Msk (0x1UL << ADC_CR_LINCALRDYW1_Pos) /*!< 0x00400000 */ +#define ADC_CR_LINCALRDYW1 ADC_CR_LINCALRDYW1_Msk /*!< ADC Linearity calibration ready Word 1 */ +#define ADC_CR_LINCALRDYW2_Pos (23U) +#define ADC_CR_LINCALRDYW2_Msk (0x1UL << ADC_CR_LINCALRDYW2_Pos) /*!< 0x00800000 */ +#define ADC_CR_LINCALRDYW2 ADC_CR_LINCALRDYW2_Msk /*!< ADC Linearity calibration ready Word 2 */ +#define ADC_CR_LINCALRDYW3_Pos (24U) +#define ADC_CR_LINCALRDYW3_Msk (0x1UL << ADC_CR_LINCALRDYW3_Pos) /*!< 0x01000000 */ +#define ADC_CR_LINCALRDYW3 ADC_CR_LINCALRDYW3_Msk /*!< ADC Linearity calibration ready Word 3 */ +#define ADC_CR_LINCALRDYW4_Pos (25U) +#define ADC_CR_LINCALRDYW4_Msk (0x1UL << ADC_CR_LINCALRDYW4_Pos) /*!< 0x02000000 */ +#define ADC_CR_LINCALRDYW4 ADC_CR_LINCALRDYW4_Msk /*!< ADC Linearity calibration ready Word 4 */ +#define ADC_CR_LINCALRDYW5_Pos (26U) +#define ADC_CR_LINCALRDYW5_Msk (0x1UL << ADC_CR_LINCALRDYW5_Pos) /*!< 0x04000000 */ +#define ADC_CR_LINCALRDYW5 ADC_CR_LINCALRDYW5_Msk /*!< ADC Linearity calibration ready Word 5 */ +#define ADC_CR_LINCALRDYW6_Pos (27U) +#define ADC_CR_LINCALRDYW6_Msk (0x1UL << ADC_CR_LINCALRDYW6_Pos) /*!< 0x08000000 */ +#define ADC_CR_LINCALRDYW6 ADC_CR_LINCALRDYW6_Msk /*!< ADC Linearity calibration ready Word 6 */ +#define ADC_CR_ADVREGEN_Pos (28U) +#define ADC_CR_ADVREGEN_Msk (0x1UL << ADC_CR_ADVREGEN_Pos) /*!< 0x10000000 */ +#define ADC_CR_ADVREGEN ADC_CR_ADVREGEN_Msk /*!< ADC Voltage regulator Enable */ +#define ADC_CR_DEEPPWD_Pos (29U) +#define ADC_CR_DEEPPWD_Msk (0x1UL << ADC_CR_DEEPPWD_Pos) /*!< 0x20000000 */ +#define ADC_CR_DEEPPWD ADC_CR_DEEPPWD_Msk /*!< ADC Deep power down Enable */ +#define ADC_CR_ADCALDIF_Pos (30U) +#define ADC_CR_ADCALDIF_Msk (0x1UL << ADC_CR_ADCALDIF_Pos) /*!< 0x40000000 */ +#define ADC_CR_ADCALDIF ADC_CR_ADCALDIF_Msk /*!< ADC Differential Mode for calibration */ +#define ADC_CR_ADCAL_Pos (31U) +#define ADC_CR_ADCAL_Msk (0x1UL << ADC_CR_ADCAL_Pos) /*!< 0x80000000 */ +#define ADC_CR_ADCAL ADC_CR_ADCAL_Msk /*!< ADC Calibration */ + +/******************** Bit definition for ADC_CFGR register ********************/ +#define ADC_CFGR_DMNGT_Pos (0U) +#define ADC_CFGR_DMNGT_Msk (0x3UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000003 */ +#define ADC_CFGR_DMNGT ADC_CFGR_DMNGT_Msk /*!< ADC Data Management configuration */ +#define ADC_CFGR_DMNGT_0 (0x1UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000001 */ +#define ADC_CFGR_DMNGT_1 (0x2UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000002 */ + +#define ADC_CFGR_RES_Pos (2U) +#define ADC_CFGR_RES_Msk (0x7UL << ADC_CFGR_RES_Pos) /*!< 0x0000001C */ +#define ADC_CFGR_RES ADC_CFGR_RES_Msk /*!< ADC Data resolution */ +#define ADC_CFGR_RES_0 (0x1UL << ADC_CFGR_RES_Pos) /*!< 0x00000004 */ +#define ADC_CFGR_RES_1 (0x2UL << ADC_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC_CFGR_RES_2 (0x4UL << ADC_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR_EXTSEL_Pos (5U) +#define ADC_CFGR_EXTSEL_Msk (0x1FUL << ADC_CFGR_EXTSEL_Pos) /*!< 0x000003E0 */ +#define ADC_CFGR_EXTSEL ADC_CFGR_EXTSEL_Msk /*!< ADC External trigger selection for regular group */ +#define ADC_CFGR_EXTSEL_0 (0x01UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_CFGR_EXTSEL_1 (0x02UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000040 */ +#define ADC_CFGR_EXTSEL_2 (0x04UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000080 */ +#define ADC_CFGR_EXTSEL_3 (0x08UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000100 */ +#define ADC_CFGR_EXTSEL_4 (0x10UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000200 */ + +#define ADC_CFGR_EXTEN_Pos (10U) +#define ADC_CFGR_EXTEN_Msk (0x3UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000C00 */ +#define ADC_CFGR_EXTEN ADC_CFGR_EXTEN_Msk /*!< ADC External trigger enable and polarity selection for regular channels */ +#define ADC_CFGR_EXTEN_0 (0x1UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000400 */ +#define ADC_CFGR_EXTEN_1 (0x2UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000800 */ + +#define ADC_CFGR_OVRMOD_Pos (12U) +#define ADC_CFGR_OVRMOD_Msk (0x1UL << ADC_CFGR_OVRMOD_Pos) /*!< 0x00001000 */ +#define ADC_CFGR_OVRMOD ADC_CFGR_OVRMOD_Msk /*!< ADC overrun mode */ +#define ADC_CFGR_CONT_Pos (13U) +#define ADC_CFGR_CONT_Msk (0x1UL << ADC_CFGR_CONT_Pos) /*!< 0x00002000 */ +#define ADC_CFGR_CONT ADC_CFGR_CONT_Msk /*!< ADC Single/continuous conversion mode for regular conversion */ +#define ADC_CFGR_AUTDLY_Pos (14U) +#define ADC_CFGR_AUTDLY_Msk (0x1UL << ADC_CFGR_AUTDLY_Pos) /*!< 0x00004000 */ +#define ADC_CFGR_AUTDLY ADC_CFGR_AUTDLY_Msk /*!< ADC Delayed conversion mode */ + +#define ADC_CFGR_DISCEN_Pos (16U) +#define ADC_CFGR_DISCEN_Msk (0x1UL << ADC_CFGR_DISCEN_Pos) /*!< 0x00010000 */ +#define ADC_CFGR_DISCEN ADC_CFGR_DISCEN_Msk /*!< ADC Discontinuous mode for regular channels */ + +#define ADC_CFGR_DISCNUM_Pos (17U) +#define ADC_CFGR_DISCNUM_Msk (0x7UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x000E0000 */ +#define ADC_CFGR_DISCNUM ADC_CFGR_DISCNUM_Msk /*!< ADC Discontinuous mode channel count */ +#define ADC_CFGR_DISCNUM_0 (0x1UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00020000 */ +#define ADC_CFGR_DISCNUM_1 (0x2UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00040000 */ +#define ADC_CFGR_DISCNUM_2 (0x4UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00080000 */ + +#define ADC_CFGR_JDISCEN_Pos (20U) +#define ADC_CFGR_JDISCEN_Msk (0x1UL << ADC_CFGR_JDISCEN_Pos) /*!< 0x00100000 */ +#define ADC_CFGR_JDISCEN ADC_CFGR_JDISCEN_Msk /*!< ADC Discontinuous mode on injected channels */ +#define ADC_CFGR_JQM_Pos (21U) +#define ADC_CFGR_JQM_Msk (0x1UL << ADC_CFGR_JQM_Pos) /*!< 0x00200000 */ +#define ADC_CFGR_JQM ADC_CFGR_JQM_Msk /*!< ADC JSQR Queue mode */ +#define ADC_CFGR_AWD1SGL_Pos (22U) +#define ADC_CFGR_AWD1SGL_Msk (0x1UL << ADC_CFGR_AWD1SGL_Pos) /*!< 0x00400000 */ +#define ADC_CFGR_AWD1SGL ADC_CFGR_AWD1SGL_Msk /*!< Enable the watchdog 1 on a single channel or on all channels */ +#define ADC_CFGR_AWD1EN_Pos (23U) +#define ADC_CFGR_AWD1EN_Msk (0x1UL << ADC_CFGR_AWD1EN_Pos) /*!< 0x00800000 */ +#define ADC_CFGR_AWD1EN ADC_CFGR_AWD1EN_Msk /*!< ADC Analog watchdog 1 enable on regular Channels */ +#define ADC_CFGR_JAWD1EN_Pos (24U) +#define ADC_CFGR_JAWD1EN_Msk (0x1UL << ADC_CFGR_JAWD1EN_Pos) /*!< 0x01000000 */ +#define ADC_CFGR_JAWD1EN ADC_CFGR_JAWD1EN_Msk /*!< ADC Analog watchdog 1 enable on injected Channels */ +#define ADC_CFGR_JAUTO_Pos (25U) +#define ADC_CFGR_JAUTO_Msk (0x1UL << ADC_CFGR_JAUTO_Pos) /*!< 0x02000000 */ +#define ADC_CFGR_JAUTO ADC_CFGR_JAUTO_Msk /*!< ADC Automatic injected group conversion */ + +#define ADC_CFGR_AWD1CH_Pos (26U) +#define ADC_CFGR_AWD1CH_Msk (0x1FUL << ADC_CFGR_AWD1CH_Pos) /*!< 0x7C000000 */ +#define ADC_CFGR_AWD1CH ADC_CFGR_AWD1CH_Msk /*!< ADC Analog watchdog 1 Channel selection */ +#define ADC_CFGR_AWD1CH_0 (0x01UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x04000000 */ +#define ADC_CFGR_AWD1CH_1 (0x02UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x08000000 */ +#define ADC_CFGR_AWD1CH_2 (0x04UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x10000000 */ +#define ADC_CFGR_AWD1CH_3 (0x08UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x20000000 */ +#define ADC_CFGR_AWD1CH_4 (0x10UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x40000000 */ + +#define ADC_CFGR_JQDIS_Pos (31U) +#define ADC_CFGR_JQDIS_Msk (0x1UL << ADC_CFGR_JQDIS_Pos) /*!< 0x80000000 */ +#define ADC_CFGR_JQDIS ADC_CFGR_JQDIS_Msk /*!< ADC Injected queue disable */ + +#define ADC3_CFGR_DMAEN_Pos (0U) +#define ADC3_CFGR_DMAEN_Msk (0x1UL << ADC3_CFGR_DMAEN_Pos) /*!< 0x00000001 */ +#define ADC3_CFGR_DMAEN ADC3_CFGR_DMAEN_Msk /*!< ADC DMA transfer enable */ +#define ADC3_CFGR_DMACFG_Pos (1U) +#define ADC3_CFGR_DMACFG_Msk (0x1UL << ADC3_CFGR_DMACFG_Pos) /*!< 0x00000002 */ +#define ADC3_CFGR_DMACFG ADC3_CFGR_DMACFG_Msk /*!< ADC DMA transfer configuration */ + +#define ADC3_CFGR_RES_Pos (3U) +#define ADC3_CFGR_RES_Msk (0x3UL << ADC3_CFGR_RES_Pos) /*!< 0x00000018 */ +#define ADC3_CFGR_RES ADC3_CFGR_RES_Msk /*!< ADC data resolution */ +#define ADC3_CFGR_RES_0 (0x1UL << ADC3_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC3_CFGR_RES_1 (0x2UL << ADC3_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC3_CFGR_ALIGN_Pos (15U) +#define ADC3_CFGR_ALIGN_Msk (0x1UL << ADC3_CFGR_ALIGN_Pos) /*!< 0x00008000 */ +#define ADC3_CFGR_ALIGN ADC3_CFGR_ALIGN_Msk /*!< ADC data alignment */ +/******************** Bit definition for ADC_CFGR2 register ********************/ +#define ADC_CFGR2_ROVSE_Pos (0U) +#define ADC_CFGR2_ROVSE_Msk (0x1UL << ADC_CFGR2_ROVSE_Pos) /*!< 0x00000001 */ +#define ADC_CFGR2_ROVSE ADC_CFGR2_ROVSE_Msk /*!< ADC Regular group oversampler enable */ +#define ADC_CFGR2_JOVSE_Pos (1U) +#define ADC_CFGR2_JOVSE_Msk (0x1UL << ADC_CFGR2_JOVSE_Pos) /*!< 0x00000002 */ +#define ADC_CFGR2_JOVSE ADC_CFGR2_JOVSE_Msk /*!< ADC Injected group oversampler enable */ + +#define ADC_CFGR2_OVSS_Pos (5U) +#define ADC_CFGR2_OVSS_Msk (0xFUL << ADC_CFGR2_OVSS_Pos) /*!< 0x000001E0 */ +#define ADC_CFGR2_OVSS ADC_CFGR2_OVSS_Msk /*!< ADC Regular Oversampling shift */ +#define ADC_CFGR2_OVSS_0 (0x1UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000020 */ +#define ADC_CFGR2_OVSS_1 (0x2UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000040 */ +#define ADC_CFGR2_OVSS_2 (0x4UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000080 */ +#define ADC_CFGR2_OVSS_3 (0x8UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000100 */ + +#define ADC_CFGR2_TROVS_Pos (9U) +#define ADC_CFGR2_TROVS_Msk (0x1UL << ADC_CFGR2_TROVS_Pos) /*!< 0x00000200 */ +#define ADC_CFGR2_TROVS ADC_CFGR2_TROVS_Msk /*!< ADC Triggered regular Oversampling */ +#define ADC_CFGR2_ROVSM_Pos (10U) +#define ADC_CFGR2_ROVSM_Msk (0x1UL << ADC_CFGR2_ROVSM_Pos) /*!< 0x00000400 */ +#define ADC_CFGR2_ROVSM ADC_CFGR2_ROVSM_Msk /*!< ADC Regular oversampling mode */ + +#define ADC_CFGR2_RSHIFT1_Pos (11U) +#define ADC_CFGR2_RSHIFT1_Msk (0x1UL << ADC_CFGR2_RSHIFT1_Pos) /*!< 0x00000800 */ +#define ADC_CFGR2_RSHIFT1 ADC_CFGR2_RSHIFT1_Msk /*!< ADC Right-shift data after Offset 1 correction */ +#define ADC_CFGR2_RSHIFT2_Pos (12U) +#define ADC_CFGR2_RSHIFT2_Msk (0x1UL << ADC_CFGR2_RSHIFT2_Pos) /*!< 0x00001000 */ +#define ADC_CFGR2_RSHIFT2 ADC_CFGR2_RSHIFT2_Msk /*!< ADC Right-shift data after Offset 2 correction */ +#define ADC_CFGR2_RSHIFT3_Pos (13U) +#define ADC_CFGR2_RSHIFT3_Msk (0x1UL << ADC_CFGR2_RSHIFT3_Pos) /*!< 0x00002000 */ +#define ADC_CFGR2_RSHIFT3 ADC_CFGR2_RSHIFT3_Msk /*!< ADC Right-shift data after Offset 3 correction */ +#define ADC_CFGR2_RSHIFT4_Pos (14U) +#define ADC_CFGR2_RSHIFT4_Msk (0x1UL << ADC_CFGR2_RSHIFT4_Pos) /*!< 0x00004000 */ +#define ADC_CFGR2_RSHIFT4 ADC_CFGR2_RSHIFT4_Msk /*!< ADC Right-shift data after Offset 4 correction */ + +#define ADC_CFGR2_OVSR_Pos (16U) +#define ADC_CFGR2_OVSR_Msk (0x3FFUL << ADC_CFGR2_OVSR_Pos) /*!< 0x03FF0000 */ +#define ADC_CFGR2_OVSR ADC_CFGR2_OVSR_Msk /*!< ADC oversampling Ratio */ +#define ADC_CFGR2_OVSR_0 (0x001UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00010000 */ +#define ADC_CFGR2_OVSR_1 (0x002UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00020000 */ +#define ADC_CFGR2_OVSR_2 (0x004UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00040000 */ +#define ADC_CFGR2_OVSR_3 (0x008UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00080000 */ +#define ADC_CFGR2_OVSR_4 (0x010UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00100000 */ +#define ADC_CFGR2_OVSR_5 (0x020UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00200000 */ +#define ADC_CFGR2_OVSR_6 (0x040UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00400000 */ +#define ADC_CFGR2_OVSR_7 (0x080UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00800000 */ +#define ADC_CFGR2_OVSR_8 (0x100UL << ADC_CFGR2_OVSR_Pos) /*!< 0x01000000 */ +#define ADC_CFGR2_OVSR_9 (0x200UL << ADC_CFGR2_OVSR_Pos) /*!< 0x02000000 */ + +#define ADC_CFGR2_LSHIFT_Pos (28U) +#define ADC_CFGR2_LSHIFT_Msk (0xFUL << ADC_CFGR2_LSHIFT_Pos) /*!< 0xF0000000 */ +#define ADC_CFGR2_LSHIFT ADC_CFGR2_LSHIFT_Msk /*!< ADC Left shift factor */ +#define ADC_CFGR2_LSHIFT_0 (0x1UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x10000000 */ +#define ADC_CFGR2_LSHIFT_1 (0x2UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x20000000 */ +#define ADC_CFGR2_LSHIFT_2 (0x4UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x40000000 */ +#define ADC_CFGR2_LSHIFT_3 (0x8UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x80000000 */ + +#define ADC3_CFGR2_OVSR_Pos (2U) +#define ADC3_CFGR2_OVSR_Msk (0x7UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x0000001C */ +#define ADC3_CFGR2_OVSR ADC3_CFGR2_OVSR_Msk /*!< ADC oversampling ratio */ +#define ADC3_CFGR2_OVSR_0 (0x1UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000004 */ +#define ADC3_CFGR2_OVSR_1 (0x2UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000008 */ +#define ADC3_CFGR2_OVSR_2 (0x4UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000010 */ + +#define ADC3_CFGR2_SWTRIG_Pos (25U) +#define ADC3_CFGR2_SWTRIG_Msk (0x1UL << ADC3_CFGR2_SWTRIG_Pos) /*!< 0x02000000 */ +#define ADC3_CFGR2_SWTRIG ADC3_CFGR2_SWTRIG_Msk /*!< ADC Software Trigger Bit for Sample time control trigger mode */ +#define ADC3_CFGR2_BULB_Pos (26U) +#define ADC3_CFGR2_BULB_Msk (0x1UL << ADC3_CFGR2_BULB_Pos) /*!< 0x04000000 */ +#define ADC3_CFGR2_BULB ADC3_CFGR2_BULB_Msk /*!< ADC Bulb sampling mode */ +#define ADC3_CFGR2_SMPTRIG_Pos (27U) +#define ADC3_CFGR2_SMPTRIG_Msk (0x1UL << ADC3_CFGR2_SMPTRIG_Pos) /*!< 0x08000000 */ +#define ADC3_CFGR2_SMPTRIG ADC3_CFGR2_SMPTRIG_Msk /*!< ADC Sample Time Control Trigger mode */ +/******************** Bit definition for ADC_SMPR1 register ********************/ +#define ADC_SMPR1_SMP0_Pos (0U) +#define ADC_SMPR1_SMP0_Msk (0x7UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000007 */ +#define ADC_SMPR1_SMP0 ADC_SMPR1_SMP0_Msk /*!< ADC Channel 0 Sampling time selection */ +#define ADC_SMPR1_SMP0_0 (0x1UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000001 */ +#define ADC_SMPR1_SMP0_1 (0x2UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000002 */ +#define ADC_SMPR1_SMP0_2 (0x4UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR1_SMP1_Pos (3U) +#define ADC_SMPR1_SMP1_Msk (0x7UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000038 */ +#define ADC_SMPR1_SMP1 ADC_SMPR1_SMP1_Msk /*!< ADC Channel 1 Sampling time selection */ +#define ADC_SMPR1_SMP1_0 (0x1UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000008 */ +#define ADC_SMPR1_SMP1_1 (0x2UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000010 */ +#define ADC_SMPR1_SMP1_2 (0x4UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR1_SMP2_Pos (6U) +#define ADC_SMPR1_SMP2_Msk (0x7UL << ADC_SMPR1_SMP2_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR1_SMP2 ADC_SMPR1_SMP2_Msk /*!< ADC Channel 2 Sampling time selection */ +#define ADC_SMPR1_SMP2_0 (0x1UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000040 */ +#define ADC_SMPR1_SMP2_1 (0x2UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000080 */ +#define ADC_SMPR1_SMP2_2 (0x4UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR1_SMP3_Pos (9U) +#define ADC_SMPR1_SMP3_Msk (0x7UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR1_SMP3 ADC_SMPR1_SMP3_Msk /*!< ADC Channel 3 Sampling time selection */ +#define ADC_SMPR1_SMP3_0 (0x1UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000200 */ +#define ADC_SMPR1_SMP3_1 (0x2UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000400 */ +#define ADC_SMPR1_SMP3_2 (0x4UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR1_SMP4_Pos (12U) +#define ADC_SMPR1_SMP4_Msk (0x7UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00007000 */ +#define ADC_SMPR1_SMP4 ADC_SMPR1_SMP4_Msk /*!< ADC Channel 4 Sampling time selection */ +#define ADC_SMPR1_SMP4_0 (0x1UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00001000 */ +#define ADC_SMPR1_SMP4_1 (0x2UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00002000 */ +#define ADC_SMPR1_SMP4_2 (0x4UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR1_SMP5_Pos (15U) +#define ADC_SMPR1_SMP5_Msk (0x7UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00038000 */ +#define ADC_SMPR1_SMP5 ADC_SMPR1_SMP5_Msk /*!< ADC Channel 5 Sampling time selection */ +#define ADC_SMPR1_SMP5_0 (0x1UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00008000 */ +#define ADC_SMPR1_SMP5_1 (0x2UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00010000 */ +#define ADC_SMPR1_SMP5_2 (0x4UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR1_SMP6_Pos (18U) +#define ADC_SMPR1_SMP6_Msk (0x7UL << ADC_SMPR1_SMP6_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR1_SMP6 ADC_SMPR1_SMP6_Msk /*!< ADC Channel 6 Sampling time selection */ +#define ADC_SMPR1_SMP6_0 (0x1UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00040000 */ +#define ADC_SMPR1_SMP6_1 (0x2UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00080000 */ +#define ADC_SMPR1_SMP6_2 (0x4UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR1_SMP7_Pos (21U) +#define ADC_SMPR1_SMP7_Msk (0x7UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR1_SMP7 ADC_SMPR1_SMP7_Msk /*!< ADC Channel 7 Sampling time selection */ +#define ADC_SMPR1_SMP7_0 (0x1UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00200000 */ +#define ADC_SMPR1_SMP7_1 (0x2UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00400000 */ +#define ADC_SMPR1_SMP7_2 (0x4UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR1_SMP8_Pos (24U) +#define ADC_SMPR1_SMP8_Msk (0x7UL << ADC_SMPR1_SMP8_Pos) /*!< 0x07000000 */ +#define ADC_SMPR1_SMP8 ADC_SMPR1_SMP8_Msk /*!< ADC Channel 8 Sampling time selection */ +#define ADC_SMPR1_SMP8_0 (0x1UL << ADC_SMPR1_SMP8_Pos) /*!< 0x01000000 */ +#define ADC_SMPR1_SMP8_1 (0x2UL << ADC_SMPR1_SMP8_Pos) /*!< 0x02000000 */ +#define ADC_SMPR1_SMP8_2 (0x4UL << ADC_SMPR1_SMP8_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR1_SMP9_Pos (27U) +#define ADC_SMPR1_SMP9_Msk (0x7UL << ADC_SMPR1_SMP9_Pos) /*!< 0x38000000 */ +#define ADC_SMPR1_SMP9 ADC_SMPR1_SMP9_Msk /*!< ADC Channel 9 Sampling time selection */ +#define ADC_SMPR1_SMP9_0 (0x1UL << ADC_SMPR1_SMP9_Pos) /*!< 0x08000000 */ +#define ADC_SMPR1_SMP9_1 (0x2UL << ADC_SMPR1_SMP9_Pos) /*!< 0x10000000 */ +#define ADC_SMPR1_SMP9_2 (0x4UL << ADC_SMPR1_SMP9_Pos) /*!< 0x20000000 */ + +/******************** Bit definition for ADC_SMPR2 register ********************/ +#define ADC_SMPR2_SMP10_Pos (0U) +#define ADC_SMPR2_SMP10_Msk (0x7UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000007 */ +#define ADC_SMPR2_SMP10 ADC_SMPR2_SMP10_Msk /*!< ADC Channel 10 Sampling time selection */ +#define ADC_SMPR2_SMP10_0 (0x1UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000001 */ +#define ADC_SMPR2_SMP10_1 (0x2UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000002 */ +#define ADC_SMPR2_SMP10_2 (0x4UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR2_SMP11_Pos (3U) +#define ADC_SMPR2_SMP11_Msk (0x7UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000038 */ +#define ADC_SMPR2_SMP11 ADC_SMPR2_SMP11_Msk /*!< ADC Channel 11 Sampling time selection */ +#define ADC_SMPR2_SMP11_0 (0x1UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000008 */ +#define ADC_SMPR2_SMP11_1 (0x2UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000010 */ +#define ADC_SMPR2_SMP11_2 (0x4UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR2_SMP12_Pos (6U) +#define ADC_SMPR2_SMP12_Msk (0x7UL << ADC_SMPR2_SMP12_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR2_SMP12 ADC_SMPR2_SMP12_Msk /*!< ADC Channel 12 Sampling time selection */ +#define ADC_SMPR2_SMP12_0 (0x1UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000040 */ +#define ADC_SMPR2_SMP12_1 (0x2UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000080 */ +#define ADC_SMPR2_SMP12_2 (0x4UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR2_SMP13_Pos (9U) +#define ADC_SMPR2_SMP13_Msk (0x7UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR2_SMP13 ADC_SMPR2_SMP13_Msk /*!< ADC Channel 13 Sampling time selection */ +#define ADC_SMPR2_SMP13_0 (0x1UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000200 */ +#define ADC_SMPR2_SMP13_1 (0x2UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000400 */ +#define ADC_SMPR2_SMP13_2 (0x4UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR2_SMP14_Pos (12U) +#define ADC_SMPR2_SMP14_Msk (0x7UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00007000 */ +#define ADC_SMPR2_SMP14 ADC_SMPR2_SMP14_Msk /*!< ADC Channel 14 Sampling time selection */ +#define ADC_SMPR2_SMP14_0 (0x1UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00001000 */ +#define ADC_SMPR2_SMP14_1 (0x2UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00002000 */ +#define ADC_SMPR2_SMP14_2 (0x4UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR2_SMP15_Pos (15U) +#define ADC_SMPR2_SMP15_Msk (0x7UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00038000 */ +#define ADC_SMPR2_SMP15 ADC_SMPR2_SMP15_Msk /*!< ADC Channel 15 Sampling time selection */ +#define ADC_SMPR2_SMP15_0 (0x1UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00008000 */ +#define ADC_SMPR2_SMP15_1 (0x2UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00010000 */ +#define ADC_SMPR2_SMP15_2 (0x4UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR2_SMP16_Pos (18U) +#define ADC_SMPR2_SMP16_Msk (0x7UL << ADC_SMPR2_SMP16_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR2_SMP16 ADC_SMPR2_SMP16_Msk /*!< ADC Channel 16 Sampling time selection */ +#define ADC_SMPR2_SMP16_0 (0x1UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00040000 */ +#define ADC_SMPR2_SMP16_1 (0x2UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00080000 */ +#define ADC_SMPR2_SMP16_2 (0x4UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR2_SMP17_Pos (21U) +#define ADC_SMPR2_SMP17_Msk (0x7UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR2_SMP17 ADC_SMPR2_SMP17_Msk /*!< ADC Channel 17 Sampling time selection */ +#define ADC_SMPR2_SMP17_0 (0x1UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00200000 */ +#define ADC_SMPR2_SMP17_1 (0x2UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00400000 */ +#define ADC_SMPR2_SMP17_2 (0x4UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR2_SMP18_Pos (24U) +#define ADC_SMPR2_SMP18_Msk (0x7UL << ADC_SMPR2_SMP18_Pos) /*!< 0x07000000 */ +#define ADC_SMPR2_SMP18 ADC_SMPR2_SMP18_Msk /*!< ADC Channel 18 Sampling time selection */ +#define ADC_SMPR2_SMP18_0 (0x1UL << ADC_SMPR2_SMP18_Pos) /*!< 0x01000000 */ +#define ADC_SMPR2_SMP18_1 (0x2UL << ADC_SMPR2_SMP18_Pos) /*!< 0x02000000 */ +#define ADC_SMPR2_SMP18_2 (0x4UL << ADC_SMPR2_SMP18_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR2_SMP19_Pos (27U) +#define ADC_SMPR2_SMP19_Msk (0x7UL << ADC_SMPR2_SMP19_Pos) /*!< 0x38000000 */ +#define ADC_SMPR2_SMP19 ADC_SMPR2_SMP19_Msk /*!< ADC Channel 19 Sampling time selection */ +#define ADC_SMPR2_SMP19_0 (0x1UL << ADC_SMPR2_SMP19_Pos) /*!< 0x08000000 */ +#define ADC_SMPR2_SMP19_1 (0x2UL << ADC_SMPR2_SMP19_Pos) /*!< 0x10000000 */ +#define ADC_SMPR2_SMP19_2 (0x4UL << ADC_SMPR2_SMP19_Pos) /*!< 0x20000000 */ + +/******************** Bit definition for ADC_PCSEL register ********************/ +#define ADC_PCSEL_PCSEL_Pos (0U) +#define ADC_PCSEL_PCSEL_Msk (0xFFFFFUL << ADC_PCSEL_PCSEL_Pos) /*!< 0x000FFFFF */ +#define ADC_PCSEL_PCSEL ADC_PCSEL_PCSEL_Msk /*!< ADC pre channel selection */ +#define ADC_PCSEL_PCSEL_0 (0x00001UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000001 */ +#define ADC_PCSEL_PCSEL_1 (0x00002UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000002 */ +#define ADC_PCSEL_PCSEL_2 (0x00004UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000004 */ +#define ADC_PCSEL_PCSEL_3 (0x00008UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000008 */ +#define ADC_PCSEL_PCSEL_4 (0x00010UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000010 */ +#define ADC_PCSEL_PCSEL_5 (0x00020UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000020 */ +#define ADC_PCSEL_PCSEL_6 (0x00040UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000040 */ +#define ADC_PCSEL_PCSEL_7 (0x00080UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000080 */ +#define ADC_PCSEL_PCSEL_8 (0x00100UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000100 */ +#define ADC_PCSEL_PCSEL_9 (0x00200UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000200 */ +#define ADC_PCSEL_PCSEL_10 (0x00400UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000400 */ +#define ADC_PCSEL_PCSEL_11 (0x00800UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000800 */ +#define ADC_PCSEL_PCSEL_12 (0x01000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00001000 */ +#define ADC_PCSEL_PCSEL_13 (0x02000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00002000 */ +#define ADC_PCSEL_PCSEL_14 (0x04000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00004000 */ +#define ADC_PCSEL_PCSEL_15 (0x08000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00008000 */ +#define ADC_PCSEL_PCSEL_16 (0x10000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00010000 */ +#define ADC_PCSEL_PCSEL_17 (0x20000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00020000 */ +#define ADC_PCSEL_PCSEL_18 (0x40000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00040000 */ +#define ADC_PCSEL_PCSEL_19 (0x80000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00080000 */ + +/***************** Bit definition for ADC_LTR1, 2, 3 registers *****************/ +#define ADC_LTR_LT_Pos (0U) +#define ADC_LTR_LT_Msk (0x3FFFFFFUL << ADC_LTR_LT_Pos) /*!< 0x03FFFFFF */ +#define ADC_LTR_LT ADC_LTR_LT_Msk /*!< ADC Analog watchdog 1, 2 and 3 lower threshold */ + +/***************** Bit definition for ADC_HTR1, 2, 3 registers ****************/ +#define ADC_HTR_HT_Pos (0U) +#define ADC_HTR_HT_Msk (0x3FFFFFFUL << ADC_HTR_HT_Pos) /*!< 0x03FFFFFF */ +#define ADC_HTR_HT ADC_HTR_HT_Msk /*!< ADC Analog watchdog 1,2 and 3 higher threshold */ + +/******************** Bit definition for ADC3_TR1 register *******************/ +#define ADC3_TR1_LT1_Pos (0U) +#define ADC3_TR1_LT1_Msk (0xFFFUL << ADC3_TR1_LT1_Pos) /*!< 0x00000FFF */ +#define ADC3_TR1_LT1 ADC3_TR1_LT1_Msk /*!< ADC analog watchdog 1 threshold low */ + +#define ADC3_TR1_AWDFILT_Pos (12U) +#define ADC3_TR1_AWDFILT_Msk (0x7UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00007000 */ +#define ADC3_TR1_AWDFILT ADC3_TR1_AWDFILT_Msk /*!< ADC analog watchdog filtering parameter */ +#define ADC3_TR1_AWDFILT_0 (0x1UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00001000 */ +#define ADC3_TR1_AWDFILT_1 (0x2UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00002000 */ +#define ADC3_TR1_AWDFILT_2 (0x4UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00004000 */ + +#define ADC3_TR1_HT1_Pos (16U) +#define ADC3_TR1_HT1_Msk (0xFFFUL << ADC3_TR1_HT1_Pos) /*!< 0x0FFF0000 */ +#define ADC3_TR1_HT1 ADC3_TR1_HT1_Msk /*!< ADC analog watchdog 1 threshold high */ + +/******************** Bit definition for ADC3_TR2 register *******************/ +#define ADC3_TR2_LT2_Pos (0U) +#define ADC3_TR2_LT2_Msk (0xFFUL << ADC3_TR2_LT2_Pos) /*!< 0x000000FF */ +#define ADC3_TR2_LT2 ADC3_TR2_LT2_Msk /*!< ADC analog watchdog 2 threshold low */ + +#define ADC3_TR2_HT2_Pos (16U) +#define ADC3_TR2_HT2_Msk (0xFFUL << ADC3_TR2_HT2_Pos) /*!< 0x00FF0000 */ +#define ADC3_TR2_HT2 ADC3_TR2_HT2_Msk /*!< ADC analog watchdog 2 threshold high */ + +/******************** Bit definition for ADC3_TR3 register *******************/ +#define ADC3_TR3_LT3_Pos (0U) +#define ADC3_TR3_LT3_Msk (0xFFUL << ADC3_TR3_LT3_Pos) /*!< 0x000000FF */ +#define ADC3_TR3_LT3 ADC3_TR3_LT3_Msk /*!< ADC analog watchdog 3 threshold low */ + +#define ADC3_TR3_HT3_Pos (16U) +#define ADC3_TR3_HT3_Msk (0xFFUL << ADC3_TR3_HT3_Pos) /*!< 0x00FF0000 */ +#define ADC3_TR3_HT3 ADC3_TR3_HT3_Msk /*!< ADC analog watchdog 3 threshold high */ + +/******************** Bit definition for ADC_SQR1 register ********************/ +#define ADC_SQR1_L_Pos (0U) +#define ADC_SQR1_L_Msk (0xFUL << ADC_SQR1_L_Pos) /*!< 0x0000000F */ +#define ADC_SQR1_L ADC_SQR1_L_Msk /*!< ADC regular channel sequence length */ +#define ADC_SQR1_L_0 (0x1UL << ADC_SQR1_L_Pos) /*!< 0x00000001 */ +#define ADC_SQR1_L_1 (0x2UL << ADC_SQR1_L_Pos) /*!< 0x00000002 */ +#define ADC_SQR1_L_2 (0x4UL << ADC_SQR1_L_Pos) /*!< 0x00000004 */ +#define ADC_SQR1_L_3 (0x8UL << ADC_SQR1_L_Pos) /*!< 0x00000008 */ + +#define ADC_SQR1_SQ1_Pos (6U) +#define ADC_SQR1_SQ1_Msk (0x1FUL << ADC_SQR1_SQ1_Pos) /*!< 0x000007C0 */ +#define ADC_SQR1_SQ1 ADC_SQR1_SQ1_Msk /*!< ADC 1st conversion in regular sequence */ +#define ADC_SQR1_SQ1_0 (0x01UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000040 */ +#define ADC_SQR1_SQ1_1 (0x02UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000080 */ +#define ADC_SQR1_SQ1_2 (0x04UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000100 */ +#define ADC_SQR1_SQ1_3 (0x08UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000200 */ +#define ADC_SQR1_SQ1_4 (0x10UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000400 */ + +#define ADC_SQR1_SQ2_Pos (12U) +#define ADC_SQR1_SQ2_Msk (0x1FUL << ADC_SQR1_SQ2_Pos) /*!< 0x0001F000 */ +#define ADC_SQR1_SQ2 ADC_SQR1_SQ2_Msk /*!< ADC 2nd conversion in regular sequence */ +#define ADC_SQR1_SQ2_0 (0x01UL << ADC_SQR1_SQ2_Pos) /*!< 0x00001000 */ +#define ADC_SQR1_SQ2_1 (0x02UL << ADC_SQR1_SQ2_Pos) /*!< 0x00002000 */ +#define ADC_SQR1_SQ2_2 (0x04UL << ADC_SQR1_SQ2_Pos) /*!< 0x00004000 */ +#define ADC_SQR1_SQ2_3 (0x08UL << ADC_SQR1_SQ2_Pos) /*!< 0x00008000 */ +#define ADC_SQR1_SQ2_4 (0x10UL << ADC_SQR1_SQ2_Pos) /*!< 0x00010000 */ + +#define ADC_SQR1_SQ3_Pos (18U) +#define ADC_SQR1_SQ3_Msk (0x1FUL << ADC_SQR1_SQ3_Pos) /*!< 0x007C0000 */ +#define ADC_SQR1_SQ3 ADC_SQR1_SQ3_Msk /*!< ADC 3rd conversion in regular sequence */ +#define ADC_SQR1_SQ3_0 (0x01UL << ADC_SQR1_SQ3_Pos) /*!< 0x00040000 */ +#define ADC_SQR1_SQ3_1 (0x02UL << ADC_SQR1_SQ3_Pos) /*!< 0x00080000 */ +#define ADC_SQR1_SQ3_2 (0x04UL << ADC_SQR1_SQ3_Pos) /*!< 0x00100000 */ +#define ADC_SQR1_SQ3_3 (0x08UL << ADC_SQR1_SQ3_Pos) /*!< 0x00200000 */ +#define ADC_SQR1_SQ3_4 (0x10UL << ADC_SQR1_SQ3_Pos) /*!< 0x00400000 */ + +#define ADC_SQR1_SQ4_Pos (24U) +#define ADC_SQR1_SQ4_Msk (0x1FUL << ADC_SQR1_SQ4_Pos) /*!< 0x1F000000 */ +#define ADC_SQR1_SQ4 ADC_SQR1_SQ4_Msk /*!< ADC 4th conversion in regular sequence */ +#define ADC_SQR1_SQ4_0 (0x01UL << ADC_SQR1_SQ4_Pos) /*!< 0x01000000 */ +#define ADC_SQR1_SQ4_1 (0x02UL << ADC_SQR1_SQ4_Pos) /*!< 0x02000000 */ +#define ADC_SQR1_SQ4_2 (0x04UL << ADC_SQR1_SQ4_Pos) /*!< 0x04000000 */ +#define ADC_SQR1_SQ4_3 (0x08UL << ADC_SQR1_SQ4_Pos) /*!< 0x08000000 */ +#define ADC_SQR1_SQ4_4 (0x10UL << ADC_SQR1_SQ4_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR2 register ********************/ +#define ADC_SQR2_SQ5_Pos (0U) +#define ADC_SQR2_SQ5_Msk (0x1FUL << ADC_SQR2_SQ5_Pos) /*!< 0x0000001F */ +#define ADC_SQR2_SQ5 ADC_SQR2_SQ5_Msk /*!< ADC 5th conversion in regular sequence */ +#define ADC_SQR2_SQ5_0 (0x01UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000001 */ +#define ADC_SQR2_SQ5_1 (0x02UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000002 */ +#define ADC_SQR2_SQ5_2 (0x04UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000004 */ +#define ADC_SQR2_SQ5_3 (0x08UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000008 */ +#define ADC_SQR2_SQ5_4 (0x10UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000010 */ + +#define ADC_SQR2_SQ6_Pos (6U) +#define ADC_SQR2_SQ6_Msk (0x1FUL << ADC_SQR2_SQ6_Pos) /*!< 0x000007C0 */ +#define ADC_SQR2_SQ6 ADC_SQR2_SQ6_Msk /*!< ADC 6th conversion in regular sequence */ +#define ADC_SQR2_SQ6_0 (0x01UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000040 */ +#define ADC_SQR2_SQ6_1 (0x02UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000080 */ +#define ADC_SQR2_SQ6_2 (0x04UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000100 */ +#define ADC_SQR2_SQ6_3 (0x08UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000200 */ +#define ADC_SQR2_SQ6_4 (0x10UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000400 */ + +#define ADC_SQR2_SQ7_Pos (12U) +#define ADC_SQR2_SQ7_Msk (0x1FUL << ADC_SQR2_SQ7_Pos) /*!< 0x0001F000 */ +#define ADC_SQR2_SQ7 ADC_SQR2_SQ7_Msk /*!< ADC 7th conversion in regular sequence */ +#define ADC_SQR2_SQ7_0 (0x01UL << ADC_SQR2_SQ7_Pos) /*!< 0x00001000 */ +#define ADC_SQR2_SQ7_1 (0x02UL << ADC_SQR2_SQ7_Pos) /*!< 0x00002000 */ +#define ADC_SQR2_SQ7_2 (0x04UL << ADC_SQR2_SQ7_Pos) /*!< 0x00004000 */ +#define ADC_SQR2_SQ7_3 (0x08UL << ADC_SQR2_SQ7_Pos) /*!< 0x00008000 */ +#define ADC_SQR2_SQ7_4 (0x10UL << ADC_SQR2_SQ7_Pos) /*!< 0x00010000 */ + +#define ADC_SQR2_SQ8_Pos (18U) +#define ADC_SQR2_SQ8_Msk (0x1FUL << ADC_SQR2_SQ8_Pos) /*!< 0x007C0000 */ +#define ADC_SQR2_SQ8 ADC_SQR2_SQ8_Msk /*!< ADC 8th conversion in regular sequence */ +#define ADC_SQR2_SQ8_0 (0x01UL << ADC_SQR2_SQ8_Pos) /*!< 0x00040000 */ +#define ADC_SQR2_SQ8_1 (0x02UL << ADC_SQR2_SQ8_Pos) /*!< 0x00080000 */ +#define ADC_SQR2_SQ8_2 (0x04UL << ADC_SQR2_SQ8_Pos) /*!< 0x00100000 */ +#define ADC_SQR2_SQ8_3 (0x08UL << ADC_SQR2_SQ8_Pos) /*!< 0x00200000 */ +#define ADC_SQR2_SQ8_4 (0x10UL << ADC_SQR2_SQ8_Pos) /*!< 0x00400000 */ + +#define ADC_SQR2_SQ9_Pos (24U) +#define ADC_SQR2_SQ9_Msk (0x1FUL << ADC_SQR2_SQ9_Pos) /*!< 0x1F000000 */ +#define ADC_SQR2_SQ9 ADC_SQR2_SQ9_Msk /*!< ADC 9th conversion in regular sequence */ +#define ADC_SQR2_SQ9_0 (0x01UL << ADC_SQR2_SQ9_Pos) /*!< 0x01000000 */ +#define ADC_SQR2_SQ9_1 (0x02UL << ADC_SQR2_SQ9_Pos) /*!< 0x02000000 */ +#define ADC_SQR2_SQ9_2 (0x04UL << ADC_SQR2_SQ9_Pos) /*!< 0x04000000 */ +#define ADC_SQR2_SQ9_3 (0x08UL << ADC_SQR2_SQ9_Pos) /*!< 0x08000000 */ +#define ADC_SQR2_SQ9_4 (0x10UL << ADC_SQR2_SQ9_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR3 register ********************/ +#define ADC_SQR3_SQ10_Pos (0U) +#define ADC_SQR3_SQ10_Msk (0x1FUL << ADC_SQR3_SQ10_Pos) /*!< 0x0000001F */ +#define ADC_SQR3_SQ10 ADC_SQR3_SQ10_Msk /*!< ADC 10th conversion in regular sequence */ +#define ADC_SQR3_SQ10_0 (0x01UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000001 */ +#define ADC_SQR3_SQ10_1 (0x02UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000002 */ +#define ADC_SQR3_SQ10_2 (0x04UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000004 */ +#define ADC_SQR3_SQ10_3 (0x08UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000008 */ +#define ADC_SQR3_SQ10_4 (0x10UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000010 */ + +#define ADC_SQR3_SQ11_Pos (6U) +#define ADC_SQR3_SQ11_Msk (0x1FUL << ADC_SQR3_SQ11_Pos) /*!< 0x000007C0 */ +#define ADC_SQR3_SQ11 ADC_SQR3_SQ11_Msk /*!< ADC 11th conversion in regular sequence */ +#define ADC_SQR3_SQ11_0 (0x01UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000040 */ +#define ADC_SQR3_SQ11_1 (0x02UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000080 */ +#define ADC_SQR3_SQ11_2 (0x04UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000100 */ +#define ADC_SQR3_SQ11_3 (0x08UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000200 */ +#define ADC_SQR3_SQ11_4 (0x10UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000400 */ + +#define ADC_SQR3_SQ12_Pos (12U) +#define ADC_SQR3_SQ12_Msk (0x1FUL << ADC_SQR3_SQ12_Pos) /*!< 0x0001F000 */ +#define ADC_SQR3_SQ12 ADC_SQR3_SQ12_Msk /*!< ADC 12th conversion in regular sequence */ +#define ADC_SQR3_SQ12_0 (0x01UL << ADC_SQR3_SQ12_Pos) /*!< 0x00001000 */ +#define ADC_SQR3_SQ12_1 (0x02UL << ADC_SQR3_SQ12_Pos) /*!< 0x00002000 */ +#define ADC_SQR3_SQ12_2 (0x04UL << ADC_SQR3_SQ12_Pos) /*!< 0x00004000 */ +#define ADC_SQR3_SQ12_3 (0x08UL << ADC_SQR3_SQ12_Pos) /*!< 0x00008000 */ +#define ADC_SQR3_SQ12_4 (0x10UL << ADC_SQR3_SQ12_Pos) /*!< 0x00010000 */ + +#define ADC_SQR3_SQ13_Pos (18U) +#define ADC_SQR3_SQ13_Msk (0x1FUL << ADC_SQR3_SQ13_Pos) /*!< 0x007C0000 */ +#define ADC_SQR3_SQ13 ADC_SQR3_SQ13_Msk /*!< ADC 13th conversion in regular sequence */ +#define ADC_SQR3_SQ13_0 (0x01UL << ADC_SQR3_SQ13_Pos) /*!< 0x00040000 */ +#define ADC_SQR3_SQ13_1 (0x02UL << ADC_SQR3_SQ13_Pos) /*!< 0x00080000 */ +#define ADC_SQR3_SQ13_2 (0x04UL << ADC_SQR3_SQ13_Pos) /*!< 0x00100000 */ +#define ADC_SQR3_SQ13_3 (0x08UL << ADC_SQR3_SQ13_Pos) /*!< 0x00200000 */ +#define ADC_SQR3_SQ13_4 (0x10UL << ADC_SQR3_SQ13_Pos) /*!< 0x00400000 */ + +#define ADC_SQR3_SQ14_Pos (24U) +#define ADC_SQR3_SQ14_Msk (0x1FUL << ADC_SQR3_SQ14_Pos) /*!< 0x1F000000 */ +#define ADC_SQR3_SQ14 ADC_SQR3_SQ14_Msk /*!< ADC 14th conversion in regular sequence */ +#define ADC_SQR3_SQ14_0 (0x01UL << ADC_SQR3_SQ14_Pos) /*!< 0x01000000 */ +#define ADC_SQR3_SQ14_1 (0x02UL << ADC_SQR3_SQ14_Pos) /*!< 0x02000000 */ +#define ADC_SQR3_SQ14_2 (0x04UL << ADC_SQR3_SQ14_Pos) /*!< 0x04000000 */ +#define ADC_SQR3_SQ14_3 (0x08UL << ADC_SQR3_SQ14_Pos) /*!< 0x08000000 */ +#define ADC_SQR3_SQ14_4 (0x10UL << ADC_SQR3_SQ14_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR4 register ********************/ +#define ADC_SQR4_SQ15_Pos (0U) +#define ADC_SQR4_SQ15_Msk (0x1FUL << ADC_SQR4_SQ15_Pos) /*!< 0x0000001F */ +#define ADC_SQR4_SQ15 ADC_SQR4_SQ15_Msk /*!< ADC 15th conversion in regular sequence */ +#define ADC_SQR4_SQ15_0 (0x01UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000001 */ +#define ADC_SQR4_SQ15_1 (0x02UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000002 */ +#define ADC_SQR4_SQ15_2 (0x04UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000004 */ +#define ADC_SQR4_SQ15_3 (0x08UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000008 */ +#define ADC_SQR4_SQ15_4 (0x10UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000010 */ + +#define ADC_SQR4_SQ16_Pos (6U) +#define ADC_SQR4_SQ16_Msk (0x1FUL << ADC_SQR4_SQ16_Pos) /*!< 0x000007C0 */ +#define ADC_SQR4_SQ16 ADC_SQR4_SQ16_Msk /*!< ADC 16th conversion in regular sequence */ +#define ADC_SQR4_SQ16_0 (0x01UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000040 */ +#define ADC_SQR4_SQ16_1 (0x02UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000080 */ +#define ADC_SQR4_SQ16_2 (0x04UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000100 */ +#define ADC_SQR4_SQ16_3 (0x08UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000200 */ +#define ADC_SQR4_SQ16_4 (0x10UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000400 */ +/******************** Bit definition for ADC_DR register ********************/ +#define ADC_DR_RDATA_Pos (0U) +#define ADC_DR_RDATA_Msk (0xFFFFFFFFUL << ADC_DR_RDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_DR_RDATA ADC_DR_RDATA_Msk /*!< ADC regular Data converted */ + +/******************** Bit definition for ADC_JSQR register ********************/ +#define ADC_JSQR_JL_Pos (0U) +#define ADC_JSQR_JL_Msk (0x3UL << ADC_JSQR_JL_Pos) /*!< 0x00000003 */ +#define ADC_JSQR_JL ADC_JSQR_JL_Msk /*!< ADC injected channel sequence length */ +#define ADC_JSQR_JL_0 (0x1UL << ADC_JSQR_JL_Pos) /*!< 0x00000001 */ +#define ADC_JSQR_JL_1 (0x2UL << ADC_JSQR_JL_Pos) /*!< 0x00000002 */ + +#define ADC_JSQR_JEXTSEL_Pos (2U) +#define ADC_JSQR_JEXTSEL_Msk (0x1FUL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x0000007C */ +#define ADC_JSQR_JEXTSEL ADC_JSQR_JEXTSEL_Msk /*!< ADC external trigger selection for injected group */ +#define ADC_JSQR_JEXTSEL_0 (0x01UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000004 */ +#define ADC_JSQR_JEXTSEL_1 (0x02UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000008 */ +#define ADC_JSQR_JEXTSEL_2 (0x04UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000010 */ +#define ADC_JSQR_JEXTSEL_3 (0x08UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_JSQR_JEXTSEL_4 (0x10UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000040 */ + +#define ADC_JSQR_JEXTEN_Pos (7U) +#define ADC_JSQR_JEXTEN_Msk (0x3UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000180 */ +#define ADC_JSQR_JEXTEN ADC_JSQR_JEXTEN_Msk /*!< ADC external trigger enable and polarity selection for injected channels */ +#define ADC_JSQR_JEXTEN_0 (0x1UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000080 */ +#define ADC_JSQR_JEXTEN_1 (0x2UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000100 */ + +#define ADC_JSQR_JSQ1_Pos (9U) +#define ADC_JSQR_JSQ1_Msk (0x1FUL << ADC_JSQR_JSQ1_Pos) /*!< 0x00003E00 */ +#define ADC_JSQR_JSQ1 ADC_JSQR_JSQ1_Msk /*!< ADC 1st conversion in injected sequence */ +#define ADC_JSQR_JSQ1_0 (0x01UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000200 */ +#define ADC_JSQR_JSQ1_1 (0x02UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000400 */ +#define ADC_JSQR_JSQ1_2 (0x04UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000800 */ +#define ADC_JSQR_JSQ1_3 (0x08UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00001000 */ +#define ADC_JSQR_JSQ1_4 (0x10UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00002000 */ + +#define ADC_JSQR_JSQ2_Pos (15U) +#define ADC_JSQR_JSQ2_Msk (0x1FUL << ADC_JSQR_JSQ2_Pos) /*!< 0x000F8000 */ +#define ADC_JSQR_JSQ2 ADC_JSQR_JSQ2_Msk /*!< ADC 2nd conversion in injected sequence */ +#define ADC_JSQR_JSQ2_0 (0x01UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00008000 */ +#define ADC_JSQR_JSQ2_1 (0x02UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00010000 */ +#define ADC_JSQR_JSQ2_2 (0x04UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00020000 */ +#define ADC_JSQR_JSQ2_3 (0x08UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00040000 */ +#define ADC_JSQR_JSQ2_4 (0x10UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00080000 */ + +#define ADC_JSQR_JSQ3_Pos (21U) +#define ADC_JSQR_JSQ3_Msk (0x1FUL << ADC_JSQR_JSQ3_Pos) /*!< 0x03E00000 */ +#define ADC_JSQR_JSQ3 ADC_JSQR_JSQ3_Msk /*!< ADC 3rd conversion in injected sequence */ +#define ADC_JSQR_JSQ3_0 (0x01UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00200000 */ +#define ADC_JSQR_JSQ3_1 (0x02UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00400000 */ +#define ADC_JSQR_JSQ3_2 (0x04UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00800000 */ +#define ADC_JSQR_JSQ3_3 (0x08UL << ADC_JSQR_JSQ3_Pos) /*!< 0x01000000 */ +#define ADC_JSQR_JSQ3_4 (0x10UL << ADC_JSQR_JSQ3_Pos) /*!< 0x02000000 */ + +#define ADC_JSQR_JSQ4_Pos (27U) +#define ADC_JSQR_JSQ4_Msk (0x1FUL << ADC_JSQR_JSQ4_Pos) /*!< 0xF8000000 */ +#define ADC_JSQR_JSQ4 ADC_JSQR_JSQ4_Msk /*!< ADC 4th conversion in injected sequence */ +#define ADC_JSQR_JSQ4_0 (0x01UL << ADC_JSQR_JSQ4_Pos) /*!< 0x08000000 */ +#define ADC_JSQR_JSQ4_1 (0x02UL << ADC_JSQR_JSQ4_Pos) /*!< 0x10000000 */ +#define ADC_JSQR_JSQ4_2 (0x04UL << ADC_JSQR_JSQ4_Pos) /*!< 0x20000000 */ +#define ADC_JSQR_JSQ4_3 (0x08UL << ADC_JSQR_JSQ4_Pos) /*!< 0x40000000 */ +#define ADC_JSQR_JSQ4_4 (0x10UL << ADC_JSQR_JSQ4_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_OFR1 register ********************/ +#define ADC_OFR1_OFFSET1_Pos (0U) +#define ADC_OFR1_OFFSET1_Msk (0x3FFFFFFUL << ADC_OFR1_OFFSET1_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR1_OFFSET1 ADC_OFR1_OFFSET1_Msk /*!< ADC data offset 1 for channel programmed into bits OFFSET1_CH[4:0] */ +#define ADC_OFR1_OFFSET1_0 (0x0000001UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000001 */ +#define ADC_OFR1_OFFSET1_1 (0x0000002UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000002 */ +#define ADC_OFR1_OFFSET1_2 (0x0000004UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000004 */ +#define ADC_OFR1_OFFSET1_3 (0x0000008UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000008 */ +#define ADC_OFR1_OFFSET1_4 (0x0000010UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000010 */ +#define ADC_OFR1_OFFSET1_5 (0x0000020UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000020 */ +#define ADC_OFR1_OFFSET1_6 (0x0000040UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000040 */ +#define ADC_OFR1_OFFSET1_7 (0x0000080UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000080 */ +#define ADC_OFR1_OFFSET1_8 (0x0000100UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000100 */ +#define ADC_OFR1_OFFSET1_9 (0x0000200UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000200 */ +#define ADC_OFR1_OFFSET1_10 (0x0000400UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000400 */ +#define ADC_OFR1_OFFSET1_11 (0x0000800UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000800 */ +#define ADC_OFR1_OFFSET1_12 (0x0001000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00001000 */ +#define ADC_OFR1_OFFSET1_13 (0x0002000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00002000 */ +#define ADC_OFR1_OFFSET1_14 (0x0004000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00004000 */ +#define ADC_OFR1_OFFSET1_15 (0x0008000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00008000 */ +#define ADC_OFR1_OFFSET1_16 (0x0010000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00010000 */ +#define ADC_OFR1_OFFSET1_17 (0x0020000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00020000 */ +#define ADC_OFR1_OFFSET1_18 (0x0040000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00040000 */ +#define ADC_OFR1_OFFSET1_19 (0x0080000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00080000 */ +#define ADC_OFR1_OFFSET1_20 (0x0100000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00100000 */ +#define ADC_OFR1_OFFSET1_21 (0x0200000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00200000 */ +#define ADC_OFR1_OFFSET1_22 (0x0400000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00400000 */ +#define ADC_OFR1_OFFSET1_23 (0x0800000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00800000 */ +#define ADC_OFR1_OFFSET1_24 (0x1000000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x01000000 */ +#define ADC_OFR1_OFFSET1_25 (0x2000000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x02000000 */ + +#define ADC_OFR1_OFFSET1_CH_Pos (26U) +#define ADC_OFR1_OFFSET1_CH_Msk (0x1FUL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR1_OFFSET1_CH ADC_OFR1_OFFSET1_CH_Msk /*!< ADC Channel selection for the data offset 1 */ +#define ADC_OFR1_OFFSET1_CH_0 (0x01UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR1_OFFSET1_CH_1 (0x02UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR1_OFFSET1_CH_2 (0x04UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR1_OFFSET1_CH_3 (0x08UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR1_OFFSET1_CH_4 (0x10UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR1_SSATE_Pos (31U) +#define ADC_OFR1_SSATE_Msk (0x1UL << ADC_OFR1_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR1_SSATE ADC_OFR1_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR1_OFFSET1_Pos (0U) +#define ADC3_OFR1_OFFSET1_Msk (0xFFFUL << ADC3_OFR1_OFFSET1_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR1_OFFSET1 ADC3_OFR1_OFFSET1_Msk /*!< ADC data offset 1 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR1_OFFSETPOS_Pos (24U) +#define ADC3_OFR1_OFFSETPOS_Msk (0x1UL << ADC3_OFR1_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR1_OFFSETPOS ADC3_OFR1_OFFSETPOS_Msk /*!< ADC offset number 1 positive */ +#define ADC3_OFR1_SATEN_Pos (25U) +#define ADC3_OFR1_SATEN_Msk (0x1UL << ADC3_OFR1_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR1_SATEN ADC3_OFR1_SATEN_Msk /*!< ADC offset number 1 saturation enable */ + +#define ADC3_OFR1_OFFSET1_EN_Pos (31U) +#define ADC3_OFR1_OFFSET1_EN_Msk (0x1UL << ADC3_OFR1_OFFSET1_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR1_OFFSET1_EN ADC3_OFR1_OFFSET1_EN_Msk /*!< ADC offset number 1 enable */ + +/******************** Bit definition for ADC_OFR2 register ********************/ +#define ADC_OFR2_OFFSET2_Pos (0U) +#define ADC_OFR2_OFFSET2_Msk (0x3FFFFFFUL << ADC_OFR2_OFFSET2_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR2_OFFSET2 ADC_OFR2_OFFSET2_Msk /*!< ADC data offset 2 for channel programmed into bits OFFSET2_CH[4:0] */ +#define ADC_OFR2_OFFSET2_0 (0x0000001UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000001 */ +#define ADC_OFR2_OFFSET2_1 (0x0000002UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000002 */ +#define ADC_OFR2_OFFSET2_2 (0x0000004UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000004 */ +#define ADC_OFR2_OFFSET2_3 (0x0000008UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000008 */ +#define ADC_OFR2_OFFSET2_4 (0x0000010UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000010 */ +#define ADC_OFR2_OFFSET2_5 (0x0000020UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000020 */ +#define ADC_OFR2_OFFSET2_6 (0x0000040UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000040 */ +#define ADC_OFR2_OFFSET2_7 (0x0000080UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000080 */ +#define ADC_OFR2_OFFSET2_8 (0x0000100UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000100 */ +#define ADC_OFR2_OFFSET2_9 (0x0000200UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000200 */ +#define ADC_OFR2_OFFSET2_10 (0x0000400UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000400 */ +#define ADC_OFR2_OFFSET2_11 (0x0000800UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000800 */ +#define ADC_OFR2_OFFSET2_12 (0x0001000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00001000 */ +#define ADC_OFR2_OFFSET2_13 (0x0002000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00002000 */ +#define ADC_OFR2_OFFSET2_14 (0x0004000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00004000 */ +#define ADC_OFR2_OFFSET2_15 (0x0008000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00008000 */ +#define ADC_OFR2_OFFSET2_16 (0x0010000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00010000 */ +#define ADC_OFR2_OFFSET2_17 (0x0020000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00020000 */ +#define ADC_OFR2_OFFSET2_18 (0x0040000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00040000 */ +#define ADC_OFR2_OFFSET2_19 (0x0080000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00080000 */ +#define ADC_OFR2_OFFSET2_20 (0x0100000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00100000 */ +#define ADC_OFR2_OFFSET2_21 (0x0200000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00200000 */ +#define ADC_OFR2_OFFSET2_22 (0x0400000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00400000 */ +#define ADC_OFR2_OFFSET2_23 (0x0800000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00800000 */ +#define ADC_OFR2_OFFSET2_24 (0x1000000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x01000000 */ +#define ADC_OFR2_OFFSET2_25 (0x2000000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x02000000 */ + +#define ADC_OFR2_OFFSET2_CH_Pos (26U) +#define ADC_OFR2_OFFSET2_CH_Msk (0x1FUL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR2_OFFSET2_CH ADC_OFR2_OFFSET2_CH_Msk /*!< ADC Channel selection for the data offset 2 */ +#define ADC_OFR2_OFFSET2_CH_0 (0x01UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR2_OFFSET2_CH_1 (0x02UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR2_OFFSET2_CH_2 (0x04UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR2_OFFSET2_CH_3 (0x08UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR2_OFFSET2_CH_4 (0x10UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR2_SSATE_Pos (31U) +#define ADC_OFR2_SSATE_Msk (0x1UL << ADC_OFR2_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR2_SSATE ADC_OFR2_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR2_OFFSET2_Pos (0U) +#define ADC3_OFR2_OFFSET2_Msk (0xFFFUL << ADC3_OFR2_OFFSET2_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR2_OFFSET2 ADC3_OFR2_OFFSET2_Msk /*!< ADC data offset 2 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR2_OFFSETPOS_Pos (24U) +#define ADC3_OFR2_OFFSETPOS_Msk (0x1UL << ADC3_OFR2_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR2_OFFSETPOS ADC3_OFR2_OFFSETPOS_Msk /*!< ADC offset number 2 positive */ +#define ADC3_OFR2_SATEN_Pos (25U) +#define ADC3_OFR2_SATEN_Msk (0x1UL << ADC3_OFR2_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR2_SATEN ADC3_OFR2_SATEN_Msk /*!< ADC offset number 2 saturation enable */ + +#define ADC3_OFR2_OFFSET2_EN_Pos (31U) +#define ADC3_OFR2_OFFSET2_EN_Msk (0x1UL << ADC3_OFR2_OFFSET2_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR2_OFFSET2_EN ADC3_OFR2_OFFSET2_EN_Msk /*!< ADC offset number 2 enable */ + +/******************** Bit definition for ADC_OFR3 register ********************/ +#define ADC_OFR3_OFFSET3_Pos (0U) +#define ADC_OFR3_OFFSET3_Msk (0x3FFFFFFUL << ADC_OFR3_OFFSET3_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR3_OFFSET3 ADC_OFR3_OFFSET3_Msk /*!< ADC data offset 3 for channel programmed into bits OFFSET3_CH[4:0] */ +#define ADC_OFR3_OFFSET3_0 (0x0000001UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000001 */ +#define ADC_OFR3_OFFSET3_1 (0x0000002UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000002 */ +#define ADC_OFR3_OFFSET3_2 (0x0000004UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000004 */ +#define ADC_OFR3_OFFSET3_3 (0x0000008UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000008 */ +#define ADC_OFR3_OFFSET3_4 (0x0000010UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000010 */ +#define ADC_OFR3_OFFSET3_5 (0x0000020UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000020 */ +#define ADC_OFR3_OFFSET3_6 (0x0000040UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000040 */ +#define ADC_OFR3_OFFSET3_7 (0x0000080UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000080 */ +#define ADC_OFR3_OFFSET3_8 (0x0000100UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000100 */ +#define ADC_OFR3_OFFSET3_9 (0x0000200UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000200 */ +#define ADC_OFR3_OFFSET3_10 (0x0000400UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000400 */ +#define ADC_OFR3_OFFSET3_11 (0x0000800UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000800 */ +#define ADC_OFR3_OFFSET3_12 (0x0001000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00001000 */ +#define ADC_OFR3_OFFSET3_13 (0x0002000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00002000 */ +#define ADC_OFR3_OFFSET3_14 (0x0004000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00004000 */ +#define ADC_OFR3_OFFSET3_15 (0x0008000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00008000 */ +#define ADC_OFR3_OFFSET3_16 (0x0010000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00010000 */ +#define ADC_OFR3_OFFSET3_17 (0x0020000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00020000 */ +#define ADC_OFR3_OFFSET3_18 (0x0040000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00040000 */ +#define ADC_OFR3_OFFSET3_19 (0x0080000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00080000 */ +#define ADC_OFR3_OFFSET3_20 (0x0100000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00100000 */ +#define ADC_OFR3_OFFSET3_21 (0x0200000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00200000 */ +#define ADC_OFR3_OFFSET3_22 (0x0400000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00400000 */ +#define ADC_OFR3_OFFSET3_23 (0x0800000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00800000 */ +#define ADC_OFR3_OFFSET3_24 (0x1000000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x01000000 */ +#define ADC_OFR3_OFFSET3_25 (0x2000000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x02000000 */ + +#define ADC_OFR3_OFFSET3_CH_Pos (26U) +#define ADC_OFR3_OFFSET3_CH_Msk (0x1FUL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR3_OFFSET3_CH ADC_OFR3_OFFSET3_CH_Msk /*!< ADC Channel selection for the data offset 3 */ +#define ADC_OFR3_OFFSET3_CH_0 (0x01UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR3_OFFSET3_CH_1 (0x02UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR3_OFFSET3_CH_2 (0x04UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR3_OFFSET3_CH_3 (0x08UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR3_OFFSET3_CH_4 (0x10UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR3_SSATE_Pos (31U) +#define ADC_OFR3_SSATE_Msk (0x1UL << ADC_OFR3_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR3_SSATE ADC_OFR3_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR3_OFFSET3_Pos (0U) +#define ADC3_OFR3_OFFSET3_Msk (0xFFFUL << ADC3_OFR3_OFFSET3_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR3_OFFSET3 ADC3_OFR3_OFFSET3_Msk /*!< ADC data offset 3 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR3_OFFSETPOS_Pos (24U) +#define ADC3_OFR3_OFFSETPOS_Msk (0x1UL << ADC3_OFR3_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR3_OFFSETPOS ADC3_OFR3_OFFSETPOS_Msk /*!< ADC offset number 3 positive */ +#define ADC3_OFR3_SATEN_Pos (25U) +#define ADC3_OFR3_SATEN_Msk (0x1UL << ADC3_OFR3_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR3_SATEN ADC3_OFR3_SATEN_Msk /*!< ADC offset number 3 saturation enable */ + +#define ADC3_OFR3_OFFSET3_EN_Pos (31U) +#define ADC3_OFR3_OFFSET3_EN_Msk (0x1UL << ADC3_OFR3_OFFSET3_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR3_OFFSET3_EN ADC3_OFR3_OFFSET3_EN_Msk /*!< ADC offset number 3 enable */ + +/******************** Bit definition for ADC_OFR4 register ********************/ +#define ADC_OFR4_OFFSET4_Pos (0U) +#define ADC_OFR4_OFFSET4_Msk (0x3FFFFFFUL << ADC_OFR4_OFFSET4_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR4_OFFSET4 ADC_OFR4_OFFSET4_Msk /*!< ADC data offset 4 for channel programmed into bits OFFSET4_CH[4:0] */ +#define ADC_OFR4_OFFSET4_0 (0x0000001UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000001 */ +#define ADC_OFR4_OFFSET4_1 (0x0000002UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000002 */ +#define ADC_OFR4_OFFSET4_2 (0x0000004UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000004 */ +#define ADC_OFR4_OFFSET4_3 (0x0000008UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000008 */ +#define ADC_OFR4_OFFSET4_4 (0x0000010UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000010 */ +#define ADC_OFR4_OFFSET4_5 (0x0000020UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000020 */ +#define ADC_OFR4_OFFSET4_6 (0x0000040UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000040 */ +#define ADC_OFR4_OFFSET4_7 (0x0000080UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000080 */ +#define ADC_OFR4_OFFSET4_8 (0x0000100UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000100 */ +#define ADC_OFR4_OFFSET4_9 (0x0000200UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000200 */ +#define ADC_OFR4_OFFSET4_10 (0x0000400UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000400 */ +#define ADC_OFR4_OFFSET4_11 (0x0000800UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000800 */ +#define ADC_OFR4_OFFSET4_12 (0x0001000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00001000 */ +#define ADC_OFR4_OFFSET4_13 (0x0002000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00002000 */ +#define ADC_OFR4_OFFSET4_14 (0x0004000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00004000 */ +#define ADC_OFR4_OFFSET4_15 (0x0008000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00008000 */ +#define ADC_OFR4_OFFSET4_16 (0x0010000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00010000 */ +#define ADC_OFR4_OFFSET4_17 (0x0020000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00020000 */ +#define ADC_OFR4_OFFSET4_18 (0x0040000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00040000 */ +#define ADC_OFR4_OFFSET4_19 (0x0080000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00080000 */ +#define ADC_OFR4_OFFSET4_20 (0x0100000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00100000 */ +#define ADC_OFR4_OFFSET4_21 (0x0200000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00200000 */ +#define ADC_OFR4_OFFSET4_22 (0x0400000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00400000 */ +#define ADC_OFR4_OFFSET4_23 (0x0800000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00800000 */ +#define ADC_OFR4_OFFSET4_24 (0x1000000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x01000000 */ +#define ADC_OFR4_OFFSET4_25 (0x2000000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x02000000 */ + +#define ADC_OFR4_OFFSET4_CH_Pos (26U) +#define ADC_OFR4_OFFSET4_CH_Msk (0x1FUL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR4_OFFSET4_CH ADC_OFR4_OFFSET4_CH_Msk /*!< ADC Channel selection for the data offset 4 */ +#define ADC_OFR4_OFFSET4_CH_0 (0x01UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR4_OFFSET4_CH_1 (0x02UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR4_OFFSET4_CH_2 (0x04UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR4_OFFSET4_CH_3 (0x08UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR4_OFFSET4_CH_4 (0x10UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR4_SSATE_Pos (31U) +#define ADC_OFR4_SSATE_Msk (0x1UL << ADC_OFR4_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR4_SSATE ADC_OFR4_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR4_OFFSET4_Pos (0U) +#define ADC3_OFR4_OFFSET4_Msk (0xFFFUL << ADC3_OFR4_OFFSET4_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR4_OFFSET4 ADC3_OFR4_OFFSET4_Msk /*!< ADC data offset 4 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR4_OFFSETPOS_Pos (24U) +#define ADC3_OFR4_OFFSETPOS_Msk (0x1UL << ADC3_OFR4_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR4_OFFSETPOS ADC3_OFR4_OFFSETPOS_Msk /*!< ADC offset number 4 positive */ +#define ADC3_OFR4_SATEN_Pos (25U) +#define ADC3_OFR4_SATEN_Msk (0x1UL << ADC3_OFR4_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR4_SATEN ADC3_OFR4_SATEN_Msk /*!< ADC offset number 4 saturation enable */ + +#define ADC3_OFR4_OFFSET4_EN_Pos (31U) +#define ADC3_OFR4_OFFSET4_EN_Msk (0x1UL << ADC3_OFR4_OFFSET4_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR4_OFFSET4_EN ADC3_OFR4_OFFSET4_EN_Msk /*!< ADC offset number 4 enable */ + +/******************** Bit definition for ADC_JDR1 register ********************/ +#define ADC_JDR1_JDATA_Pos (0U) +#define ADC_JDR1_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR1_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR1_JDATA ADC_JDR1_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR1_JDATA_0 (0x00000001UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR1_JDATA_1 (0x00000002UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR1_JDATA_2 (0x00000004UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR1_JDATA_3 (0x00000008UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR1_JDATA_4 (0x00000010UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR1_JDATA_5 (0x00000020UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR1_JDATA_6 (0x00000040UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR1_JDATA_7 (0x00000080UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR1_JDATA_8 (0x00000100UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR1_JDATA_9 (0x00000200UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR1_JDATA_10 (0x00000400UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR1_JDATA_11 (0x00000800UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR1_JDATA_12 (0x00001000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR1_JDATA_13 (0x00002000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR1_JDATA_14 (0x00004000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR1_JDATA_15 (0x00008000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR1_JDATA_16 (0x00010000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR1_JDATA_17 (0x00020000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR1_JDATA_18 (0x00040000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR1_JDATA_19 (0x00080000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR1_JDATA_20 (0x00100000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR1_JDATA_21 (0x00200000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR1_JDATA_22 (0x00400000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR1_JDATA_23 (0x00800000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR1_JDATA_24 (0x01000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR1_JDATA_25 (0x02000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR1_JDATA_26 (0x04000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR1_JDATA_27 (0x08000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR1_JDATA_28 (0x10000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR1_JDATA_29 (0x20000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR1_JDATA_30 (0x40000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR1_JDATA_31 (0x80000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR2 register ********************/ +#define ADC_JDR2_JDATA_Pos (0U) +#define ADC_JDR2_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR2_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR2_JDATA ADC_JDR2_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR2_JDATA_0 (0x00000001UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR2_JDATA_1 (0x00000002UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR2_JDATA_2 (0x00000004UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR2_JDATA_3 (0x00000008UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR2_JDATA_4 (0x00000010UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR2_JDATA_5 (0x00000020UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR2_JDATA_6 (0x00000040UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR2_JDATA_7 (0x00000080UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR2_JDATA_8 (0x00000100UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR2_JDATA_9 (0x00000200UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR2_JDATA_10 (0x00000400UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR2_JDATA_11 (0x00000800UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR2_JDATA_12 (0x00001000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR2_JDATA_13 (0x00002000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR2_JDATA_14 (0x00004000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR2_JDATA_15 (0x00008000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR2_JDATA_16 (0x00010000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR2_JDATA_17 (0x00020000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR2_JDATA_18 (0x00040000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR2_JDATA_19 (0x00080000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR2_JDATA_20 (0x00100000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR2_JDATA_21 (0x00200000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR2_JDATA_22 (0x00400000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR2_JDATA_23 (0x00800000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR2_JDATA_24 (0x01000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR2_JDATA_25 (0x02000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR2_JDATA_26 (0x04000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR2_JDATA_27 (0x08000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR2_JDATA_28 (0x10000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR2_JDATA_29 (0x20000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR2_JDATA_30 (0x40000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR2_JDATA_31 (0x80000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR3 register ********************/ +#define ADC_JDR3_JDATA_Pos (0U) +#define ADC_JDR3_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR3_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR3_JDATA ADC_JDR3_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR3_JDATA_0 (0x00000001UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR3_JDATA_1 (0x00000002UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR3_JDATA_2 (0x00000004UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR3_JDATA_3 (0x00000008UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR3_JDATA_4 (0x00000010UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR3_JDATA_5 (0x00000020UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR3_JDATA_6 (0x00000040UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR3_JDATA_7 (0x00000080UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR3_JDATA_8 (0x00000100UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR3_JDATA_9 (0x00000200UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR3_JDATA_10 (0x00000400UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR3_JDATA_11 (0x00000800UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR3_JDATA_12 (0x00001000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR3_JDATA_13 (0x00002000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR3_JDATA_14 (0x00004000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR3_JDATA_15 (0x00008000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR3_JDATA_16 (0x00010000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR3_JDATA_17 (0x00020000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR3_JDATA_18 (0x00040000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR3_JDATA_19 (0x00080000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR3_JDATA_20 (0x00100000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR3_JDATA_21 (0x00200000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR3_JDATA_22 (0x00400000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR3_JDATA_23 (0x00800000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR3_JDATA_24 (0x01000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR3_JDATA_25 (0x02000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR3_JDATA_26 (0x04000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR3_JDATA_27 (0x08000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR3_JDATA_28 (0x10000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR3_JDATA_29 (0x20000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR3_JDATA_30 (0x40000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR3_JDATA_31 (0x80000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR4 register ********************/ +#define ADC_JDR4_JDATA_Pos (0U) +#define ADC_JDR4_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR4_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR4_JDATA ADC_JDR4_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR4_JDATA_0 (0x00000001UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR4_JDATA_1 (0x00000002UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR4_JDATA_2 (0x00000004UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR4_JDATA_3 (0x00000008UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR4_JDATA_4 (0x00000010UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR4_JDATA_5 (0x00000020UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR4_JDATA_6 (0x00000040UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR4_JDATA_7 (0x00000080UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR4_JDATA_8 (0x00000100UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR4_JDATA_9 (0x00000200UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR4_JDATA_10 (0x00000400UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR4_JDATA_11 (0x00000800UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR4_JDATA_12 (0x00001000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR4_JDATA_13 (0x00002000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR4_JDATA_14 (0x00004000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR4_JDATA_15 (0x00008000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR4_JDATA_16 (0x00010000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR4_JDATA_17 (0x00020000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR4_JDATA_18 (0x00040000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR4_JDATA_19 (0x00080000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR4_JDATA_20 (0x00100000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR4_JDATA_21 (0x00200000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR4_JDATA_22 (0x00400000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR4_JDATA_23 (0x00800000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR4_JDATA_24 (0x01000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR4_JDATA_25 (0x02000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR4_JDATA_26 (0x04000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR4_JDATA_27 (0x08000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR4_JDATA_28 (0x10000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR4_JDATA_29 (0x20000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR4_JDATA_30 (0x40000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR4_JDATA_31 (0x80000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_AWD2CR register ********************/ +#define ADC_AWD2CR_AWD2CH_Pos (0U) +#define ADC_AWD2CR_AWD2CH_Msk (0xFFFFFUL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x000FFFFF */ +#define ADC_AWD2CR_AWD2CH ADC_AWD2CR_AWD2CH_Msk /*!< ADC Analog watchdog 2 channel selection */ +#define ADC_AWD2CR_AWD2CH_0 (0x00001UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD2CR_AWD2CH_1 (0x00002UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD2CR_AWD2CH_2 (0x00004UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD2CR_AWD2CH_3 (0x00008UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD2CR_AWD2CH_4 (0x00010UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD2CR_AWD2CH_5 (0x00020UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD2CR_AWD2CH_6 (0x00040UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD2CR_AWD2CH_7 (0x00080UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD2CR_AWD2CH_8 (0x00100UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD2CR_AWD2CH_9 (0x00200UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD2CR_AWD2CH_10 (0x00400UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD2CR_AWD2CH_11 (0x00800UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD2CR_AWD2CH_12 (0x01000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD2CR_AWD2CH_13 (0x02000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD2CR_AWD2CH_14 (0x04000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD2CR_AWD2CH_15 (0x08000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD2CR_AWD2CH_16 (0x10000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD2CR_AWD2CH_17 (0x20000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD2CR_AWD2CH_18 (0x40000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00040000 */ +#define ADC_AWD2CR_AWD2CH_19 (0x80000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_AWD3CR register ********************/ +#define ADC_AWD3CR_AWD3CH_Pos (0U) +#define ADC_AWD3CR_AWD3CH_Msk (0xFFFFFUL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x000FFFFF */ +#define ADC_AWD3CR_AWD3CH ADC_AWD3CR_AWD3CH_Msk /*!< ADC Analog watchdog 2 channel selection */ +#define ADC_AWD3CR_AWD3CH_0 (0x00001UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD3CR_AWD3CH_1 (0x00002UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD3CR_AWD3CH_2 (0x00004UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD3CR_AWD3CH_3 (0x00008UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD3CR_AWD3CH_4 (0x00010UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD3CR_AWD3CH_5 (0x00020UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD3CR_AWD3CH_6 (0x00040UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD3CR_AWD3CH_7 (0x00080UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD3CR_AWD3CH_8 (0x00100UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD3CR_AWD3CH_9 (0x00200UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD3CR_AWD3CH_10 (0x00400UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD3CR_AWD3CH_11 (0x00800UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD3CR_AWD3CH_12 (0x01000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD3CR_AWD3CH_13 (0x02000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD3CR_AWD3CH_14 (0x04000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD3CR_AWD3CH_15 (0x08000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD3CR_AWD3CH_16 (0x10000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD3CR_AWD3CH_17 (0x20000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD3CR_AWD3CH_18 (0x40000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00040000 */ +#define ADC_AWD3CR_AWD3CH_19 (0x80000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_DIFSEL register ********************/ +#define ADC_DIFSEL_DIFSEL_Pos (0U) +#define ADC_DIFSEL_DIFSEL_Msk (0xFFFFFUL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x000FFFFF */ +#define ADC_DIFSEL_DIFSEL ADC_DIFSEL_DIFSEL_Msk /*!< ADC differential modes for channels 1 to 18 */ +#define ADC_DIFSEL_DIFSEL_0 (0x00001UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000001 */ +#define ADC_DIFSEL_DIFSEL_1 (0x00002UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000002 */ +#define ADC_DIFSEL_DIFSEL_2 (0x00004UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000004 */ +#define ADC_DIFSEL_DIFSEL_3 (0x00008UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000008 */ +#define ADC_DIFSEL_DIFSEL_4 (0x00010UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000010 */ +#define ADC_DIFSEL_DIFSEL_5 (0x00020UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000020 */ +#define ADC_DIFSEL_DIFSEL_6 (0x00040UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000040 */ +#define ADC_DIFSEL_DIFSEL_7 (0x00080UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000080 */ +#define ADC_DIFSEL_DIFSEL_8 (0x00100UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000100 */ +#define ADC_DIFSEL_DIFSEL_9 (0x00200UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000200 */ +#define ADC_DIFSEL_DIFSEL_10 (0x00400UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000400 */ +#define ADC_DIFSEL_DIFSEL_11 (0x00800UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000800 */ +#define ADC_DIFSEL_DIFSEL_12 (0x01000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00001000 */ +#define ADC_DIFSEL_DIFSEL_13 (0x02000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00002000 */ +#define ADC_DIFSEL_DIFSEL_14 (0x04000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00004000 */ +#define ADC_DIFSEL_DIFSEL_15 (0x08000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00008000 */ +#define ADC_DIFSEL_DIFSEL_16 (0x10000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00010000 */ +#define ADC_DIFSEL_DIFSEL_17 (0x20000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00020000 */ +#define ADC_DIFSEL_DIFSEL_18 (0x40000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00040000 */ +#define ADC_DIFSEL_DIFSEL_19 (0x80000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_CALFACT register ********************/ +#define ADC_CALFACT_CALFACT_S_Pos (0U) +#define ADC_CALFACT_CALFACT_S_Msk (0x7FFUL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x000007FF */ +#define ADC_CALFACT_CALFACT_S ADC_CALFACT_CALFACT_S_Msk /*!< ADC calibration factors in single-ended mode */ +#define ADC_CALFACT_CALFACT_S_0 (0x001UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT_CALFACT_S_1 (0x002UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT_CALFACT_S_2 (0x004UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT_CALFACT_S_3 (0x008UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT_CALFACT_S_4 (0x010UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT_CALFACT_S_5 (0x020UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT_CALFACT_S_6 (0x040UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000040 */ +#define ADC_CALFACT_CALFACT_S_7 (0x080UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000080 */ +#define ADC_CALFACT_CALFACT_S_8 (0x100UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000100 */ +#define ADC_CALFACT_CALFACT_S_9 (0x200UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000200 */ +#define ADC_CALFACT_CALFACT_S_10 (0x400UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000400 */ +#define ADC_CALFACT_CALFACT_D_Pos (16U) +#define ADC_CALFACT_CALFACT_D_Msk (0x7FFUL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x07FF0000 */ +#define ADC_CALFACT_CALFACT_D ADC_CALFACT_CALFACT_D_Msk /*!< ADC calibration factors in differential mode */ +#define ADC_CALFACT_CALFACT_D_0 (0x001UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT_CALFACT_D_1 (0x002UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT_CALFACT_D_2 (0x004UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT_CALFACT_D_3 (0x008UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT_CALFACT_D_4 (0x010UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT_CALFACT_D_5 (0x020UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT_CALFACT_D_6 (0x040UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00400000 */ +#define ADC_CALFACT_CALFACT_D_7 (0x080UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00800000 */ +#define ADC_CALFACT_CALFACT_D_8 (0x100UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x01000000 */ +#define ADC_CALFACT_CALFACT_D_9 (0x200UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x02000000 */ +#define ADC_CALFACT_CALFACT_D_10 (0x400UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x04000000 */ + +/******************** Bit definition for ADC_CALFACT2 register ********************/ +#define ADC_CALFACT2_LINCALFACT_Pos (0U) +#define ADC_CALFACT2_LINCALFACT_Msk (0x3FFFFFFFUL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x3FFFFFFF */ +#define ADC_CALFACT2_LINCALFACT ADC_CALFACT2_LINCALFACT_Msk /*!< ADC Linearity calibration factors */ +#define ADC_CALFACT2_LINCALFACT_0 (0x00000001UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT2_LINCALFACT_1 (0x00000002UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT2_LINCALFACT_2 (0x00000004UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT2_LINCALFACT_3 (0x00000008UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT2_LINCALFACT_4 (0x00000010UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT2_LINCALFACT_5 (0x00000020UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT2_LINCALFACT_6 (0x00000040UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000040 */ +#define ADC_CALFACT2_LINCALFACT_7 (0x00000080UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000080 */ +#define ADC_CALFACT2_LINCALFACT_8 (0x00000100UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000100 */ +#define ADC_CALFACT2_LINCALFACT_9 (0x00000200UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000200 */ +#define ADC_CALFACT2_LINCALFACT_10 (0x00000400UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000400 */ +#define ADC_CALFACT2_LINCALFACT_11 (0x00000800UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000800 */ +#define ADC_CALFACT2_LINCALFACT_12 (0x00001000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00001000 */ +#define ADC_CALFACT2_LINCALFACT_13 (0x00002000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00002000 */ +#define ADC_CALFACT2_LINCALFACT_14 (0x00004000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00004000 */ +#define ADC_CALFACT2_LINCALFACT_15 (0x00008000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00008000 */ +#define ADC_CALFACT2_LINCALFACT_16 (0x00010000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT2_LINCALFACT_17 (0x00020000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT2_LINCALFACT_18 (0x00040000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT2_LINCALFACT_19 (0x00080000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT2_LINCALFACT_20 (0x00100000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT2_LINCALFACT_21 (0x00200000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT2_LINCALFACT_22 (0x00400000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00400000 */ +#define ADC_CALFACT2_LINCALFACT_23 (0x00800000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00800000 */ +#define ADC_CALFACT2_LINCALFACT_24 (0x01000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x01000000 */ +#define ADC_CALFACT2_LINCALFACT_25 (0x02000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x02000000 */ +#define ADC_CALFACT2_LINCALFACT_26 (0x04000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x04000000 */ +#define ADC_CALFACT2_LINCALFACT_27 (0x08000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x08000000 */ +#define ADC_CALFACT2_LINCALFACT_28 (0x10000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x10000000 */ +#define ADC_CALFACT2_LINCALFACT_29 (0x20000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x20000000 */ + +/************************* ADC Common registers *****************************/ +/******************** Bit definition for ADC_CSR register ********************/ +#define ADC_CSR_ADRDY_MST_Pos (0U) +#define ADC_CSR_ADRDY_MST_Msk (0x1UL << ADC_CSR_ADRDY_MST_Pos) /*!< 0x00000001 */ +#define ADC_CSR_ADRDY_MST ADC_CSR_ADRDY_MST_Msk /*!< Master ADC ready */ +#define ADC_CSR_EOSMP_MST_Pos (1U) +#define ADC_CSR_EOSMP_MST_Msk (0x1UL << ADC_CSR_EOSMP_MST_Pos) /*!< 0x00000002 */ +#define ADC_CSR_EOSMP_MST ADC_CSR_EOSMP_MST_Msk /*!< End of sampling phase flag of the master ADC */ +#define ADC_CSR_EOC_MST_Pos (2U) +#define ADC_CSR_EOC_MST_Msk (0x1UL << ADC_CSR_EOC_MST_Pos) /*!< 0x00000004 */ +#define ADC_CSR_EOC_MST ADC_CSR_EOC_MST_Msk /*!< End of regular conversion of the master ADC */ +#define ADC_CSR_EOS_MST_Pos (3U) +#define ADC_CSR_EOS_MST_Msk (0x1UL << ADC_CSR_EOS_MST_Pos) /*!< 0x00000008 */ +#define ADC_CSR_EOS_MST ADC_CSR_EOS_MST_Msk /*!< End of regular sequence flag of the master ADC */ +#define ADC_CSR_OVR_MST_Pos (4U) +#define ADC_CSR_OVR_MST_Msk (0x1UL << ADC_CSR_OVR_MST_Pos) /*!< 0x00000010 */ +#define ADC_CSR_OVR_MST ADC_CSR_OVR_MST_Msk /*!< Overrun flag of the master ADC */ +#define ADC_CSR_JEOC_MST_Pos (5U) +#define ADC_CSR_JEOC_MST_Msk (0x1UL << ADC_CSR_JEOC_MST_Pos) /*!< 0x00000020 */ +#define ADC_CSR_JEOC_MST ADC_CSR_JEOC_MST_Msk /*!< End of injected conversion of the master ADC */ +#define ADC_CSR_JEOS_MST_Pos (6U) +#define ADC_CSR_JEOS_MST_Msk (0x1UL << ADC_CSR_JEOS_MST_Pos) /*!< 0x00000040 */ +#define ADC_CSR_JEOS_MST ADC_CSR_JEOS_MST_Msk /*!< End of injected sequence flag of the master ADC */ +#define ADC_CSR_AWD1_MST_Pos (7U) +#define ADC_CSR_AWD1_MST_Msk (0x1UL << ADC_CSR_AWD1_MST_Pos) /*!< 0x00000080 */ +#define ADC_CSR_AWD1_MST ADC_CSR_AWD1_MST_Msk /*!< Analog watchdog 1 flag of the master ADC */ +#define ADC_CSR_AWD2_MST_Pos (8U) +#define ADC_CSR_AWD2_MST_Msk (0x1UL << ADC_CSR_AWD2_MST_Pos) /*!< 0x00000100 */ +#define ADC_CSR_AWD2_MST ADC_CSR_AWD2_MST_Msk /*!< Analog watchdog 2 flag of the master ADC */ +#define ADC_CSR_AWD3_MST_Pos (9U) +#define ADC_CSR_AWD3_MST_Msk (0x1UL << ADC_CSR_AWD3_MST_Pos) /*!< 0x00000200 */ +#define ADC_CSR_AWD3_MST ADC_CSR_AWD3_MST_Msk /*!< Analog watchdog 3 flag of the master ADC */ +#define ADC_CSR_JQOVF_MST_Pos (10U) +#define ADC_CSR_JQOVF_MST_Msk (0x1UL << ADC_CSR_JQOVF_MST_Pos) /*!< 0x00000400 */ +#define ADC_CSR_JQOVF_MST ADC_CSR_JQOVF_MST_Msk /*!< Injected context queue overflow flag of the master ADC */ +#define ADC_CSR_ADRDY_SLV_Pos (16U) +#define ADC_CSR_ADRDY_SLV_Msk (0x1UL << ADC_CSR_ADRDY_SLV_Pos) /*!< 0x00010000 */ +#define ADC_CSR_ADRDY_SLV ADC_CSR_ADRDY_SLV_Msk /*!< Slave ADC ready */ +#define ADC_CSR_EOSMP_SLV_Pos (17U) +#define ADC_CSR_EOSMP_SLV_Msk (0x1UL << ADC_CSR_EOSMP_SLV_Pos) /*!< 0x00020000 */ +#define ADC_CSR_EOSMP_SLV ADC_CSR_EOSMP_SLV_Msk /*!< End of sampling phase flag of the slave ADC */ +#define ADC_CSR_EOC_SLV_Pos (18U) +#define ADC_CSR_EOC_SLV_Msk (0x1UL << ADC_CSR_EOC_SLV_Pos) /*!< 0x00040000 */ +#define ADC_CSR_EOC_SLV ADC_CSR_EOC_SLV_Msk /*!< End of regular conversion of the slave ADC */ +#define ADC_CSR_EOS_SLV_Pos (19U) +#define ADC_CSR_EOS_SLV_Msk (0x1UL << ADC_CSR_EOS_SLV_Pos) /*!< 0x00080000 */ +#define ADC_CSR_EOS_SLV ADC_CSR_EOS_SLV_Msk /*!< End of regular sequence flag of the slave ADC */ +#define ADC_CSR_OVR_SLV_Pos (20U) +#define ADC_CSR_OVR_SLV_Msk (0x1UL << ADC_CSR_OVR_SLV_Pos) /*!< 0x00100000 */ +#define ADC_CSR_OVR_SLV ADC_CSR_OVR_SLV_Msk /*!< Overrun flag of the slave ADC */ +#define ADC_CSR_JEOC_SLV_Pos (21U) +#define ADC_CSR_JEOC_SLV_Msk (0x1UL << ADC_CSR_JEOC_SLV_Pos) /*!< 0x00200000 */ +#define ADC_CSR_JEOC_SLV ADC_CSR_JEOC_SLV_Msk /*!< End of injected conversion of the slave ADC */ +#define ADC_CSR_JEOS_SLV_Pos (22U) +#define ADC_CSR_JEOS_SLV_Msk (0x1UL << ADC_CSR_JEOS_SLV_Pos) /*!< 0x00400000 */ +#define ADC_CSR_JEOS_SLV ADC_CSR_JEOS_SLV_Msk /*!< End of injected sequence flag of the slave ADC */ +#define ADC_CSR_AWD1_SLV_Pos (23U) +#define ADC_CSR_AWD1_SLV_Msk (0x1UL << ADC_CSR_AWD1_SLV_Pos) /*!< 0x00800000 */ +#define ADC_CSR_AWD1_SLV ADC_CSR_AWD1_SLV_Msk /*!< Analog watchdog 1 flag of the slave ADC */ +#define ADC_CSR_AWD2_SLV_Pos (24U) +#define ADC_CSR_AWD2_SLV_Msk (0x1UL << ADC_CSR_AWD2_SLV_Pos) /*!< 0x01000000 */ +#define ADC_CSR_AWD2_SLV ADC_CSR_AWD2_SLV_Msk /*!< Analog watchdog 2 flag of the slave ADC */ +#define ADC_CSR_AWD3_SLV_Pos (25U) +#define ADC_CSR_AWD3_SLV_Msk (0x1UL << ADC_CSR_AWD3_SLV_Pos) /*!< 0x02000000 */ +#define ADC_CSR_AWD3_SLV ADC_CSR_AWD3_SLV_Msk /*!< Analog watchdog 3 flag of the slave ADC */ +#define ADC_CSR_JQOVF_SLV_Pos (26U) +#define ADC_CSR_JQOVF_SLV_Msk (0x1UL << ADC_CSR_JQOVF_SLV_Pos) /*!< 0x04000000 */ +#define ADC_CSR_JQOVF_SLV ADC_CSR_JQOVF_SLV_Msk /*!< Injected context queue overflow flag of the slave ADC */ + +/******************** Bit definition for ADC_CCR register ********************/ +#define ADC_CCR_DUAL_Pos (0U) +#define ADC_CCR_DUAL_Msk (0x1FUL << ADC_CCR_DUAL_Pos) /*!< 0x0000001F */ +#define ADC_CCR_DUAL ADC_CCR_DUAL_Msk /*!< Dual ADC mode selection */ +#define ADC_CCR_DUAL_0 (0x01UL << ADC_CCR_DUAL_Pos) /*!< 0x00000001 */ +#define ADC_CCR_DUAL_1 (0x02UL << ADC_CCR_DUAL_Pos) /*!< 0x00000002 */ +#define ADC_CCR_DUAL_2 (0x04UL << ADC_CCR_DUAL_Pos) /*!< 0x00000004 */ +#define ADC_CCR_DUAL_3 (0x08UL << ADC_CCR_DUAL_Pos) /*!< 0x00000008 */ +#define ADC_CCR_DUAL_4 (0x10UL << ADC_CCR_DUAL_Pos) /*!< 0x00000010 */ + +#define ADC_CCR_DELAY_Pos (8U) +#define ADC_CCR_DELAY_Msk (0xFUL << ADC_CCR_DELAY_Pos) /*!< 0x00000F00 */ +#define ADC_CCR_DELAY ADC_CCR_DELAY_Msk /*!< Delay between 2 sampling phases */ +#define ADC_CCR_DELAY_0 (0x1UL << ADC_CCR_DELAY_Pos) /*!< 0x00000100 */ +#define ADC_CCR_DELAY_1 (0x2UL << ADC_CCR_DELAY_Pos) /*!< 0x00000200 */ +#define ADC_CCR_DELAY_2 (0x4UL << ADC_CCR_DELAY_Pos) /*!< 0x00000400 */ +#define ADC_CCR_DELAY_3 (0x8UL << ADC_CCR_DELAY_Pos) /*!< 0x00000800 */ + + +#define ADC_CCR_DAMDF_Pos (14U) +#define ADC_CCR_DAMDF_Msk (0x3UL << ADC_CCR_DAMDF_Pos) /*!< 0x0000C000 */ +#define ADC_CCR_DAMDF ADC_CCR_DAMDF_Msk /*!< Dual ADC mode Data format */ +#define ADC_CCR_DAMDF_0 (0x1UL << ADC_CCR_DAMDF_Pos) /*!< 0x00004000 */ +#define ADC_CCR_DAMDF_1 (0x2UL << ADC_CCR_DAMDF_Pos) /*!< 0x00008000 */ + +#define ADC_CCR_CKMODE_Pos (16U) +#define ADC_CCR_CKMODE_Msk (0x3UL << ADC_CCR_CKMODE_Pos) /*!< 0x00030000 */ +#define ADC_CCR_CKMODE ADC_CCR_CKMODE_Msk /*!< ADC clock mode */ +#define ADC_CCR_CKMODE_0 (0x1UL << ADC_CCR_CKMODE_Pos) /*!< 0x00010000 */ +#define ADC_CCR_CKMODE_1 (0x2UL << ADC_CCR_CKMODE_Pos) /*!< 0x00020000 */ + +#define ADC_CCR_PRESC_Pos (18U) +#define ADC_CCR_PRESC_Msk (0xFUL << ADC_CCR_PRESC_Pos) /*!< 0x003C0000 */ +#define ADC_CCR_PRESC ADC_CCR_PRESC_Msk /*!< ADC prescaler */ +#define ADC_CCR_PRESC_0 (0x1UL << ADC_CCR_PRESC_Pos) /*!< 0x00040000 */ +#define ADC_CCR_PRESC_1 (0x2UL << ADC_CCR_PRESC_Pos) /*!< 0x00080000 */ +#define ADC_CCR_PRESC_2 (0x4UL << ADC_CCR_PRESC_Pos) /*!< 0x00100000 */ +#define ADC_CCR_PRESC_3 (0x8UL << ADC_CCR_PRESC_Pos) /*!< 0x00200000 */ + +#define ADC_CCR_VREFEN_Pos (22U) +#define ADC_CCR_VREFEN_Msk (0x1UL << ADC_CCR_VREFEN_Pos) /*!< 0x00400000 */ +#define ADC_CCR_VREFEN ADC_CCR_VREFEN_Msk /*!< VREFINT enable */ +#define ADC_CCR_TSEN_Pos (23U) +#define ADC_CCR_TSEN_Msk (0x1UL << ADC_CCR_TSEN_Pos) /*!< 0x00800000 */ +#define ADC_CCR_TSEN ADC_CCR_TSEN_Msk /*!< Temperature sensor enable */ +#define ADC_CCR_VBATEN_Pos (24U) +#define ADC_CCR_VBATEN_Msk (0x1UL << ADC_CCR_VBATEN_Pos) /*!< 0x01000000 */ +#define ADC_CCR_VBATEN ADC_CCR_VBATEN_Msk /*!< VBAT enable */ + +/******************** Bit definition for ADC_CDR register *******************/ +#define ADC_CDR_RDATA_MST_Pos (0U) +#define ADC_CDR_RDATA_MST_Msk (0xFFFFUL << ADC_CDR_RDATA_MST_Pos) /*!< 0x0000FFFF */ +#define ADC_CDR_RDATA_MST ADC_CDR_RDATA_MST_Msk /*!< ADC multimode master group regular conversion data */ + +#define ADC_CDR_RDATA_SLV_Pos (16U) +#define ADC_CDR_RDATA_SLV_Msk (0xFFFFUL << ADC_CDR_RDATA_SLV_Pos) /*!< 0xFFFF0000 */ +#define ADC_CDR_RDATA_SLV ADC_CDR_RDATA_SLV_Msk /*!< ADC multimode slave group regular conversion data */ + +/******************** Bit definition for ADC_CDR2 register ******************/ +#define ADC_CDR2_RDATA_ALT_Pos (0U) +#define ADC_CDR2_RDATA_ALT_Msk (0xFFFFFFFFUL << ADC_CDR2_RDATA_ALT_Pos) /*!< 0xFFFFFFFF */ +#define ADC_CDR2_RDATA_ALT ADC_CDR2_RDATA_ALT_Msk /*!< Regular data of the master/slave alternated ADCs */ + + +/******************************************************************************/ +/* */ +/* VREFBUF */ +/* */ +/******************************************************************************/ +/******************* Bit definition for VREFBUF_CSR register ****************/ +#define VREFBUF_CSR_ENVR_Pos (0U) +#define VREFBUF_CSR_ENVR_Msk (0x1UL << VREFBUF_CSR_ENVR_Pos) /*!< 0x00000001 */ +#define VREFBUF_CSR_ENVR VREFBUF_CSR_ENVR_Msk /*!*/ +#define DAC_CR_CEN1_Pos (14U) +#define DAC_CR_CEN1_Msk (0x1UL << DAC_CR_CEN1_Pos) /*!< 0x00004000 */ +#define DAC_CR_CEN1 DAC_CR_CEN1_Msk /*!*/ + +#define DAC_CR_EN2_Pos (16U) +#define DAC_CR_EN2_Msk (0x1UL << DAC_CR_EN2_Pos) /*!< 0x00010000 */ +#define DAC_CR_EN2 DAC_CR_EN2_Msk /*!*/ +#define DAC_CR_CEN2_Pos (30U) +#define DAC_CR_CEN2_Msk (0x1UL << DAC_CR_CEN2_Pos) /*!< 0x40000000 */ +#define DAC_CR_CEN2 DAC_CR_CEN2_Msk /*!*/ + +/***************** Bit definition for DAC_SWTRIGR register ******************/ +#define DAC_SWTRIGR_SWTRIG1_Pos (0U) +#define DAC_SWTRIGR_SWTRIG1_Msk (0x1UL << DAC_SWTRIGR_SWTRIG1_Pos) /*!< 0x00000001 */ +#define DAC_SWTRIGR_SWTRIG1 DAC_SWTRIGR_SWTRIG1_Msk /*!
© Copyright (c) 2019 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32h735xx + * @{ + */ + +#ifndef STM32H735xx_H +#define STM32H735xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32H7XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M Processor Exceptions Numbers *****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 4 Cortex-M Memory Management Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt ( wwdg1_it, wwdg2_it) */ + PVD_AVD_IRQn = 1, /*!< PVD/AVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1 and ADC2 global Interrupts */ + FDCAN1_IT0_IRQn = 19, /*!< FDCAN1 Interrupt line 0 */ + FDCAN2_IT0_IRQn = 20, /*!< FDCAN2 Interrupt line 0 */ + FDCAN1_IT1_IRQn = 21, /*!< FDCAN1 Interrupt line 1 */ + FDCAN2_IT1_IRQn = 22, /*!< FDCAN2 Interrupt line 1 */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */ + TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */ + TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + FMC_IRQn = 48, /*!< FMC global Interrupt */ + SDMMC1_IRQn = 49, /*!< SDMMC1 global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + ETH_IRQn = 61, /*!< Ethernet global Interrupt */ + ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */ + FDCAN_CAL_IRQn = 63, /*!< FDCAN Calibration unit Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */ + OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */ + OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */ + OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */ + DCMI_PSSI_IRQn = 78, /*!< DCMI and PSSI global interrupt */ + CRYP_IRQn = 79, /*!< CRYP crypto global interrupt */ + HASH_RNG_IRQn = 80, /*!< HASH and RNG global interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SPI6_IRQn = 86, /*!< SPI6 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + LTDC_IRQn = 88, /*!< LTDC global Interrupt */ + LTDC_ER_IRQn = 89, /*!< LTDC Error global Interrupt */ + DMA2D_IRQn = 90, /*!< DMA2D global Interrupt */ + OCTOSPI1_IRQn = 92, /*!< OCTOSPI1 global interrupt */ + LPTIM1_IRQn = 93, /*!< LP TIM1 interrupt */ + CEC_IRQn = 94, /*!< HDMI-CEC global Interrupt */ + I2C4_EV_IRQn = 95, /*!< I2C4 Event Interrupt */ + I2C4_ER_IRQn = 96, /*!< I2C4 Error Interrupt */ + SPDIF_RX_IRQn = 97, /*!< SPDIF-RX global Interrupt */ + DMAMUX1_OVR_IRQn = 102, /*! + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + __IO uint32_t CFGR2; /*!< ADC Configuration register 2, Address offset: 0x10 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + __IO uint32_t PCSEL_RES0; /*!< Rserved for ADC3, ADC1/2 pre-channel selection, Address offset: 0x1C */ + __IO uint32_t LTR1_TR1; /*!< ADC watchdog Lower threshold register 1, Address offset: 0x20 */ + __IO uint32_t HTR1_TR2; /*!< ADC watchdog higher threshold register 1, Address offset: 0x24 */ + __IO uint32_t RES1_TR3; /*!< Rserved for ADC1/2, ADC3 threshold register, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t LTR2_DIFSEL; /*!< ADC watchdog Lower threshold register 2, Difsel for ADC3, Address offset: 0xB0 */ + __IO uint32_t HTR2_CALFACT; /*!< ADC watchdog Higher threshold register 2, Calfact for ADC3, Address offset: 0xB4 */ + __IO uint32_t LTR3_RES10; /*!< ADC watchdog Lower threshold register 3, specific ADC1/2, Address offset: 0xB8 */ + __IO uint32_t HTR3_RES11; /*!< ADC watchdog Higher threshold register 3, specific ADC1/2, Address offset: 0xBC */ + __IO uint32_t DIFSEL_RES12; /*!< ADC Differential Mode Selection Register specific ADC1/2, Address offset: 0xC0 */ + __IO uint32_t CALFACT_RES13; /*!< ADC Calibration Factors specific ADC1/2, Address offset: 0xC4 */ + __IO uint32_t CALFACT2_RES14; /*!< ADC Linearity Calibration Factors specific ADC1/2, Address offset: 0xC8 */ +} ADC_TypeDef; + + +typedef struct +{ +__IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ +uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ +__IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ +__IO uint32_t CDR; /*!< ADC common regular data register for dual Address offset: ADC1/3 base address + 0x30C */ +__IO uint32_t CDR2; /*!< ADC common regular data register for 32-bit dual mode Address offset: ADC1/3 base address + 0x310 */ + +} ADC_Common_TypeDef; + + +/** + * @brief VREFBUF + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< VREFBUF control and status register, Address offset: 0x00 */ + __IO uint32_t CCR; /*!< VREFBUF calibration and control register, Address offset: 0x04 */ +} VREFBUF_TypeDef; + + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< FDCAN Core Release register, Address offset: 0x000 */ + __IO uint32_t ENDN; /*!< FDCAN Endian register, Address offset: 0x004 */ + __IO uint32_t RESERVED1; /*!< Reserved, 0x008 */ + __IO uint32_t DBTP; /*!< FDCAN Data Bit Timing & Prescaler register, Address offset: 0x00C */ + __IO uint32_t TEST; /*!< FDCAN Test register, Address offset: 0x010 */ + __IO uint32_t RWD; /*!< FDCAN RAM Watchdog register, Address offset: 0x014 */ + __IO uint32_t CCCR; /*!< FDCAN CC Control register, Address offset: 0x018 */ + __IO uint32_t NBTP; /*!< FDCAN Nominal Bit Timing & Prescaler register, Address offset: 0x01C */ + __IO uint32_t TSCC; /*!< FDCAN Timestamp Counter Configuration register, Address offset: 0x020 */ + __IO uint32_t TSCV; /*!< FDCAN Timestamp Counter Value register, Address offset: 0x024 */ + __IO uint32_t TOCC; /*!< FDCAN Timeout Counter Configuration register, Address offset: 0x028 */ + __IO uint32_t TOCV; /*!< FDCAN Timeout Counter Value register, Address offset: 0x02C */ + __IO uint32_t RESERVED2[4]; /*!< Reserved, 0x030 - 0x03C */ + __IO uint32_t ECR; /*!< FDCAN Error Counter register, Address offset: 0x040 */ + __IO uint32_t PSR; /*!< FDCAN Protocol Status register, Address offset: 0x044 */ + __IO uint32_t TDCR; /*!< FDCAN Transmitter Delay Compensation register, Address offset: 0x048 */ + __IO uint32_t RESERVED3; /*!< Reserved, 0x04C */ + __IO uint32_t IR; /*!< FDCAN Interrupt register, Address offset: 0x050 */ + __IO uint32_t IE; /*!< FDCAN Interrupt Enable register, Address offset: 0x054 */ + __IO uint32_t ILS; /*!< FDCAN Interrupt Line Select register, Address offset: 0x058 */ + __IO uint32_t ILE; /*!< FDCAN Interrupt Line Enable register, Address offset: 0x05C */ + __IO uint32_t RESERVED4[8]; /*!< Reserved, 0x060 - 0x07C */ + __IO uint32_t GFC; /*!< FDCAN Global Filter Configuration register, Address offset: 0x080 */ + __IO uint32_t SIDFC; /*!< FDCAN Standard ID Filter Configuration register, Address offset: 0x084 */ + __IO uint32_t XIDFC; /*!< FDCAN Extended ID Filter Configuration register, Address offset: 0x088 */ + __IO uint32_t RESERVED5; /*!< Reserved, 0x08C */ + __IO uint32_t XIDAM; /*!< FDCAN Extended ID AND Mask register, Address offset: 0x090 */ + __IO uint32_t HPMS; /*!< FDCAN High Priority Message Status register, Address offset: 0x094 */ + __IO uint32_t NDAT1; /*!< FDCAN New Data 1 register, Address offset: 0x098 */ + __IO uint32_t NDAT2; /*!< FDCAN New Data 2 register, Address offset: 0x09C */ + __IO uint32_t RXF0C; /*!< FDCAN Rx FIFO 0 Configuration register, Address offset: 0x0A0 */ + __IO uint32_t RXF0S; /*!< FDCAN Rx FIFO 0 Status register, Address offset: 0x0A4 */ + __IO uint32_t RXF0A; /*!< FDCAN Rx FIFO 0 Acknowledge register, Address offset: 0x0A8 */ + __IO uint32_t RXBC; /*!< FDCAN Rx Buffer Configuration register, Address offset: 0x0AC */ + __IO uint32_t RXF1C; /*!< FDCAN Rx FIFO 1 Configuration register, Address offset: 0x0B0 */ + __IO uint32_t RXF1S; /*!< FDCAN Rx FIFO 1 Status register, Address offset: 0x0B4 */ + __IO uint32_t RXF1A; /*!< FDCAN Rx FIFO 1 Acknowledge register, Address offset: 0x0B8 */ + __IO uint32_t RXESC; /*!< FDCAN Rx Buffer/FIFO Element Size Configuration register, Address offset: 0x0BC */ + __IO uint32_t TXBC; /*!< FDCAN Tx Buffer Configuration register, Address offset: 0x0C0 */ + __IO uint32_t TXFQS; /*!< FDCAN Tx FIFO/Queue Status register, Address offset: 0x0C4 */ + __IO uint32_t TXESC; /*!< FDCAN Tx Buffer Element Size Configuration register, Address offset: 0x0C8 */ + __IO uint32_t TXBRP; /*!< FDCAN Tx Buffer Request Pending register, Address offset: 0x0CC */ + __IO uint32_t TXBAR; /*!< FDCAN Tx Buffer Add Request register, Address offset: 0x0D0 */ + __IO uint32_t TXBCR; /*!< FDCAN Tx Buffer Cancellation Request register, Address offset: 0x0D4 */ + __IO uint32_t TXBTO; /*!< FDCAN Tx Buffer Transmission Occurred register, Address offset: 0x0D8 */ + __IO uint32_t TXBCF; /*!< FDCAN Tx Buffer Cancellation Finished register, Address offset: 0x0DC */ + __IO uint32_t TXBTIE; /*!< FDCAN Tx Buffer Transmission Interrupt Enable register, Address offset: 0x0E0 */ + __IO uint32_t TXBCIE; /*!< FDCAN Tx Buffer Cancellation Finished Interrupt Enable register, Address offset: 0x0E4 */ + __IO uint32_t RESERVED6[2]; /*!< Reserved, 0x0E8 - 0x0EC */ + __IO uint32_t TXEFC; /*!< FDCAN Tx Event FIFO Configuration register, Address offset: 0x0F0 */ + __IO uint32_t TXEFS; /*!< FDCAN Tx Event FIFO Status register, Address offset: 0x0F4 */ + __IO uint32_t TXEFA; /*!< FDCAN Tx Event FIFO Acknowledge register, Address offset: 0x0F8 */ + __IO uint32_t RESERVED7; /*!< Reserved, 0x0FC */ +} FDCAN_GlobalTypeDef; + +/** + * @brief TTFD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t TTTMC; /*!< TT Trigger Memory Configuration register, Address offset: 0x100 */ + __IO uint32_t TTRMC; /*!< TT Reference Message Configuration register, Address offset: 0x104 */ + __IO uint32_t TTOCF; /*!< TT Operation Configuration register, Address offset: 0x108 */ + __IO uint32_t TTMLM; /*!< TT Matrix Limits register, Address offset: 0x10C */ + __IO uint32_t TURCF; /*!< TUR Configuration register, Address offset: 0x110 */ + __IO uint32_t TTOCN; /*!< TT Operation Control register, Address offset: 0x114 */ + __IO uint32_t TTGTP; /*!< TT Global Time Preset register, Address offset: 0x118 */ + __IO uint32_t TTTMK; /*!< TT Time Mark register, Address offset: 0x11C */ + __IO uint32_t TTIR; /*!< TT Interrupt register, Address offset: 0x120 */ + __IO uint32_t TTIE; /*!< TT Interrupt Enable register, Address offset: 0x124 */ + __IO uint32_t TTILS; /*!< TT Interrupt Line Select register, Address offset: 0x128 */ + __IO uint32_t TTOST; /*!< TT Operation Status register, Address offset: 0x12C */ + __IO uint32_t TURNA; /*!< TT TUR Numerator Actual register, Address offset: 0x130 */ + __IO uint32_t TTLGT; /*!< TT Local and Global Time register, Address offset: 0x134 */ + __IO uint32_t TTCTC; /*!< TT Cycle Time and Count register, Address offset: 0x138 */ + __IO uint32_t TTCPT; /*!< TT Capture Time register, Address offset: 0x13C */ + __IO uint32_t TTCSM; /*!< TT Cycle Sync Mark register, Address offset: 0x140 */ + __IO uint32_t RESERVED1[111]; /*!< Reserved, 0x144 - 0x2FC */ + __IO uint32_t TTTS; /*!< TT Trigger Select register, Address offset: 0x300 */ +} TTCAN_TypeDef; + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< Clock Calibration Unit Core Release register, Address offset: 0x00 */ + __IO uint32_t CCFG; /*!< Calibration Configuration register, Address offset: 0x04 */ + __IO uint32_t CSTAT; /*!< Calibration Status register, Address offset: 0x08 */ + __IO uint32_t CWD; /*!< Calibration Watchdog register, Address offset: 0x0C */ + __IO uint32_t IR; /*!< CCU Interrupt register, Address offset: 0x10 */ + __IO uint32_t IE; /*!< CCU Interrupt Enable register, Address offset: 0x14 */ +} FDCAN_ClockCalibrationUnit_TypeDef; + + +/** + * @brief Consumer Electronics Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< CEC control register, Address offset:0x00 */ + __IO uint32_t CFGR; /*!< CEC configuration register, Address offset:0x04 */ + __IO uint32_t TXDR; /*!< CEC Tx data register , Address offset:0x08 */ + __IO uint32_t RXDR; /*!< CEC Rx Data Register, Address offset:0x0C */ + __IO uint32_t ISR; /*!< CEC Interrupt and Status Register, Address offset:0x10 */ + __IO uint32_t IER; /*!< CEC interrupt enable register, Address offset:0x14 */ +}CEC_TypeDef; + +/** + * @brief COordincate Rotation DIgital Computer + */ +typedef struct +{ + __IO uint32_t CSR; /*!< CORDIC control and status register, Address offset: 0x00 */ + __IO uint32_t WDATA; /*!< CORDIC argument register, Address offset: 0x04 */ + __IO uint32_t RDATA; /*!< CORDIC result register, Address offset: 0x08 */ +} CORDIC_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint32_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + + +/** + * @brief Clock Recovery System + */ +typedef struct +{ +__IO uint32_t CR; /*!< CRS ccontrol register, Address offset: 0x00 */ +__IO uint32_t CFGR; /*!< CRS configuration register, Address offset: 0x04 */ +__IO uint32_t ISR; /*!< CRS interrupt and status register, Address offset: 0x08 */ +__IO uint32_t ICR; /*!< CRS interrupt flag clear register, Address offset: 0x0C */ +} CRS_TypeDef; + + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ + __IO uint32_t CCR; /*!< DAC calibration control register, Address offset: 0x38 */ + __IO uint32_t MCR; /*!< DAC mode control register, Address offset: 0x3C */ + __IO uint32_t SHSR1; /*!< DAC Sample and Hold sample time register 1, Address offset: 0x40 */ + __IO uint32_t SHSR2; /*!< DAC Sample and Hold sample time register 2, Address offset: 0x44 */ + __IO uint32_t SHHR; /*!< DAC Sample and Hold hold time register, Address offset: 0x48 */ + __IO uint32_t SHRR; /*!< DAC Sample and Hold refresh time register, Address offset: 0x4C */ +} DAC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Debug MCU + */ +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + uint32_t RESERVED4[11]; /*!< Reserved, Address offset: 0x08 */ + __IO uint32_t APB3FZ1; /*!< Debug MCU APB3FZ1 freeze register, Address offset: 0x34 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x38 */ + __IO uint32_t APB1LFZ1; /*!< Debug MCU APB1LFZ1 freeze register, Address offset: 0x3C */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x40 */ + __IO uint32_t APB1HFZ1; /*!< Debug MCU APB1LFZ1 freeze register, Address offset: 0x44 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x48 */ + __IO uint32_t APB2FZ1; /*!< Debug MCU APB2FZ1 freeze register, Address offset: 0x4C */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0x50 */ + __IO uint32_t APB4FZ1; /*!< Debug MCU APB4FZ1 freeze register, Address offset: 0x54 */ + __IO uint32_t RESERVED9[990]; /*!< Reserved, Address offset: 0x58-0xFCC */ + __IO uint32_t PIDR4; /*!< Debug MCU peripheral identity register 4, Address offset: 0xFD0 */ + __IO uint32_t RESERVED10[3];/*!< Reserved, Address offset: 0xFD4-0xFDC */ + __IO uint32_t PIDR0; /*!< Debug MCU peripheral identity register 0, Address offset: 0xFE0 */ + __IO uint32_t PIDR1; /*!< Debug MCU peripheral identity register 1, Address offset: 0xFE4 */ + __IO uint32_t PIDR2; /*!< Debug MCU peripheral identity register 2, Address offset: 0xFE8 */ + __IO uint32_t PIDR3; /*!< Debug MCU peripheral identity register 3, Address offset: 0xFEC */ + __IO uint32_t CIDR0; /*!< Debug MCU component identity register 0, Address offset: 0xFF0 */ + __IO uint32_t CIDR1; /*!< Debug MCU component identity register 1, Address offset: 0xFF4 */ + __IO uint32_t CIDR2; /*!< Debug MCU component identity register 2, Address offset: 0xFF8 */ + __IO uint32_t CIDR3; /*!< Debug MCU component identity register 3, Address offset: 0xFFC */ +}DBGMCU_TypeDef; +/** + * @brief DCMI + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DCMI control register 1, Address offset: 0x00 */ + __IO uint32_t SR; /*!< DCMI status register, Address offset: 0x04 */ + __IO uint32_t RISR; /*!< DCMI raw interrupt status register, Address offset: 0x08 */ + __IO uint32_t IER; /*!< DCMI interrupt enable register, Address offset: 0x0C */ + __IO uint32_t MISR; /*!< DCMI masked interrupt status register, Address offset: 0x10 */ + __IO uint32_t ICR; /*!< DCMI interrupt clear register, Address offset: 0x14 */ + __IO uint32_t ESCR; /*!< DCMI embedded synchronization code register, Address offset: 0x18 */ + __IO uint32_t ESUR; /*!< DCMI embedded synchronization unmask register, Address offset: 0x1C */ + __IO uint32_t CWSTRTR; /*!< DCMI crop window start, Address offset: 0x20 */ + __IO uint32_t CWSIZER; /*!< DCMI crop window size, Address offset: 0x24 */ + __IO uint32_t DR; /*!< DCMI data register, Address offset: 0x28 */ +} DCMI_TypeDef; + +/** + * @brief PSSI + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PSSI control register 1, Address offset: 0x000 */ + __IO uint32_t SR; /*!< PSSI status register, Address offset: 0x004 */ + __IO uint32_t RIS; /*!< PSSI raw interrupt status register, Address offset: 0x008 */ + __IO uint32_t IER; /*!< PSSI interrupt enable register, Address offset: 0x00C */ + __IO uint32_t MIS; /*!< PSSI masked interrupt status register, Address offset: 0x010 */ + __IO uint32_t ICR; /*!< PSSI interrupt clear register, Address offset: 0x014 */ + __IO uint32_t RESERVED1[4]; /*!< Reserved, 0x018 - 0x024 */ + __IO uint32_t DR; /*!< PSSI data register, Address offset: 0x028 */ + __IO uint32_t RESERVED2[241]; /*!< Reserved, 0x02C - 0x3EC */ + __IO uint32_t HWCFGR; /*!< PSSI IP HW configuration register, Address offset: 0x3F0 */ + __IO uint32_t VERR; /*!< PSSI IP version register, Address offset: 0x3F4 */ + __IO uint32_t IPIDR; /*!< PSSI IP ID register, Address offset: 0x3F8 */ + __IO uint32_t SIDR; /*!< PSSI SIZE ID register, Address offset: 0x3FC */ +} PSSI_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CM0AR; /*!< DMA channel x memory 0 address register */ + __IO uint32_t CM1AR; /*!< DMA channel x memory 1 address register */ +} BDMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} BDMA_TypeDef; + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA Multiplexer Channel x Control Register */ +}DMAMUX_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< DMA Channel Status Register */ + __IO uint32_t CFR; /*!< DMA Channel Clear Flag Register */ +}DMAMUX_ChannelStatus_TypeDef; + +typedef struct +{ + __IO uint32_t RGCR; /*!< DMA Request Generator x Control Register */ +}DMAMUX_RequestGen_TypeDef; + +typedef struct +{ + __IO uint32_t RGSR; /*!< DMA Request Generator Status Register */ + __IO uint32_t RGCFR; /*!< DMA Request Generator Clear Flag Register */ +}DMAMUX_RequestGenStatus_TypeDef; + +/** + * @brief MDMA Controller + */ +typedef struct +{ + __IO uint32_t GISR0; /*!< MDMA Global Interrupt/Status Register 0, Address offset: 0x00 */ +}MDMA_TypeDef; + +typedef struct +{ + __IO uint32_t CISR; /*!< MDMA channel x interrupt/status register, Address offset: 0x40 */ + __IO uint32_t CIFCR; /*!< MDMA channel x interrupt flag clear register, Address offset: 0x44 */ + __IO uint32_t CESR; /*!< MDMA Channel x error status register, Address offset: 0x48 */ + __IO uint32_t CCR; /*!< MDMA channel x control register, Address offset: 0x4C */ + __IO uint32_t CTCR; /*!< MDMA channel x Transfer Configuration register, Address offset: 0x50 */ + __IO uint32_t CBNDTR; /*!< MDMA Channel x block number of data register, Address offset: 0x54 */ + __IO uint32_t CSAR; /*!< MDMA channel x source address register, Address offset: 0x58 */ + __IO uint32_t CDAR; /*!< MDMA channel x destination address register, Address offset: 0x5C */ + __IO uint32_t CBRUR; /*!< MDMA channel x Block Repeat address Update register, Address offset: 0x60 */ + __IO uint32_t CLAR; /*!< MDMA channel x Link Address register, Address offset: 0x64 */ + __IO uint32_t CTBR; /*!< MDMA channel x Trigger and Bus selection Register, Address offset: 0x68 */ + uint32_t RESERVED0; /*!< Reserved, 0x6C */ + __IO uint32_t CMAR; /*!< MDMA channel x Mask address register, Address offset: 0x70 */ + __IO uint32_t CMDR; /*!< MDMA channel x Mask Data register, Address offset: 0x74 */ +}MDMA_Channel_TypeDef; + +/** + * @brief DMA2D Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA2D Control Register, Address offset: 0x00 */ + __IO uint32_t ISR; /*!< DMA2D Interrupt Status Register, Address offset: 0x04 */ + __IO uint32_t IFCR; /*!< DMA2D Interrupt Flag Clear Register, Address offset: 0x08 */ + __IO uint32_t FGMAR; /*!< DMA2D Foreground Memory Address Register, Address offset: 0x0C */ + __IO uint32_t FGOR; /*!< DMA2D Foreground Offset Register, Address offset: 0x10 */ + __IO uint32_t BGMAR; /*!< DMA2D Background Memory Address Register, Address offset: 0x14 */ + __IO uint32_t BGOR; /*!< DMA2D Background Offset Register, Address offset: 0x18 */ + __IO uint32_t FGPFCCR; /*!< DMA2D Foreground PFC Control Register, Address offset: 0x1C */ + __IO uint32_t FGCOLR; /*!< DMA2D Foreground Color Register, Address offset: 0x20 */ + __IO uint32_t BGPFCCR; /*!< DMA2D Background PFC Control Register, Address offset: 0x24 */ + __IO uint32_t BGCOLR; /*!< DMA2D Background Color Register, Address offset: 0x28 */ + __IO uint32_t FGCMAR; /*!< DMA2D Foreground CLUT Memory Address Register, Address offset: 0x2C */ + __IO uint32_t BGCMAR; /*!< DMA2D Background CLUT Memory Address Register, Address offset: 0x30 */ + __IO uint32_t OPFCCR; /*!< DMA2D Output PFC Control Register, Address offset: 0x34 */ + __IO uint32_t OCOLR; /*!< DMA2D Output Color Register, Address offset: 0x38 */ + __IO uint32_t OMAR; /*!< DMA2D Output Memory Address Register, Address offset: 0x3C */ + __IO uint32_t OOR; /*!< DMA2D Output Offset Register, Address offset: 0x40 */ + __IO uint32_t NLR; /*!< DMA2D Number of Line Register, Address offset: 0x44 */ + __IO uint32_t LWR; /*!< DMA2D Line Watermark Register, Address offset: 0x48 */ + __IO uint32_t AMTCR; /*!< DMA2D AHB Master Timer Configuration Register, Address offset: 0x4C */ + uint32_t RESERVED[236]; /*!< Reserved, 0x50-0x3FF */ + __IO uint32_t FGCLUT[256]; /*!< DMA2D Foreground CLUT, Address offset:400-7FF */ + __IO uint32_t BGCLUT[256]; /*!< DMA2D Background CLUT, Address offset:800-BFF */ +} DMA2D_TypeDef; + + +/** + * @brief Ethernet MAC + */ +typedef struct +{ + __IO uint32_t MACCR; + __IO uint32_t MACECR; + __IO uint32_t MACPFR; + __IO uint32_t MACWTR; + __IO uint32_t MACHT0R; + __IO uint32_t MACHT1R; + uint32_t RESERVED1[14]; + __IO uint32_t MACVTR; + uint32_t RESERVED2; + __IO uint32_t MACVHTR; + uint32_t RESERVED3; + __IO uint32_t MACVIR; + __IO uint32_t MACIVIR; + uint32_t RESERVED4[2]; + __IO uint32_t MACTFCR; + uint32_t RESERVED5[7]; + __IO uint32_t MACRFCR; + uint32_t RESERVED6[7]; + __IO uint32_t MACISR; + __IO uint32_t MACIER; + __IO uint32_t MACRXTXSR; + uint32_t RESERVED7; + __IO uint32_t MACPCSR; + __IO uint32_t MACRWKPFR; + uint32_t RESERVED8[2]; + __IO uint32_t MACLCSR; + __IO uint32_t MACLTCR; + __IO uint32_t MACLETR; + __IO uint32_t MAC1USTCR; + uint32_t RESERVED9[12]; + __IO uint32_t MACVR; + __IO uint32_t MACDR; + uint32_t RESERVED10; + __IO uint32_t MACHWF0R; + __IO uint32_t MACHWF1R; + __IO uint32_t MACHWF2R; + uint32_t RESERVED11[54]; + __IO uint32_t MACMDIOAR; + __IO uint32_t MACMDIODR; + uint32_t RESERVED12[2]; + __IO uint32_t MACARPAR; + uint32_t RESERVED13[59]; + __IO uint32_t MACA0HR; + __IO uint32_t MACA0LR; + __IO uint32_t MACA1HR; + __IO uint32_t MACA1LR; + __IO uint32_t MACA2HR; + __IO uint32_t MACA2LR; + __IO uint32_t MACA3HR; + __IO uint32_t MACA3LR; + uint32_t RESERVED14[248]; + __IO uint32_t MMCCR; + __IO uint32_t MMCRIR; + __IO uint32_t MMCTIR; + __IO uint32_t MMCRIMR; + __IO uint32_t MMCTIMR; + uint32_t RESERVED15[14]; + __IO uint32_t MMCTSCGPR; + __IO uint32_t MMCTMCGPR; + uint32_t RESERVED16[5]; + __IO uint32_t MMCTPCGR; + uint32_t RESERVED17[10]; + __IO uint32_t MMCRCRCEPR; + __IO uint32_t MMCRAEPR; + uint32_t RESERVED18[10]; + __IO uint32_t MMCRUPGR; + uint32_t RESERVED19[9]; + __IO uint32_t MMCTLPIMSTR; + __IO uint32_t MMCTLPITCR; + __IO uint32_t MMCRLPIMSTR; + __IO uint32_t MMCRLPITCR; + uint32_t RESERVED20[65]; + __IO uint32_t MACL3L4C0R; + __IO uint32_t MACL4A0R; + uint32_t RESERVED21[2]; + __IO uint32_t MACL3A0R0R; + __IO uint32_t MACL3A1R0R; + __IO uint32_t MACL3A2R0R; + __IO uint32_t MACL3A3R0R; + uint32_t RESERVED22[4]; + __IO uint32_t MACL3L4C1R; + __IO uint32_t MACL4A1R; + uint32_t RESERVED23[2]; + __IO uint32_t MACL3A0R1R; + __IO uint32_t MACL3A1R1R; + __IO uint32_t MACL3A2R1R; + __IO uint32_t MACL3A3R1R; + uint32_t RESERVED24[108]; + __IO uint32_t MACTSCR; + __IO uint32_t MACSSIR; + __IO uint32_t MACSTSR; + __IO uint32_t MACSTNR; + __IO uint32_t MACSTSUR; + __IO uint32_t MACSTNUR; + __IO uint32_t MACTSAR; + uint32_t RESERVED25; + __IO uint32_t MACTSSR; + uint32_t RESERVED26[3]; + __IO uint32_t MACTTSSNR; + __IO uint32_t MACTTSSSR; + uint32_t RESERVED27[2]; + __IO uint32_t MACACR; + uint32_t RESERVED28; + __IO uint32_t MACATSNR; + __IO uint32_t MACATSSR; + __IO uint32_t MACTSIACR; + __IO uint32_t MACTSEACR; + __IO uint32_t MACTSICNR; + __IO uint32_t MACTSECNR; + uint32_t RESERVED29[4]; + __IO uint32_t MACPPSCR; + uint32_t RESERVED30[3]; + __IO uint32_t MACPPSTTSR; + __IO uint32_t MACPPSTTNR; + __IO uint32_t MACPPSIR; + __IO uint32_t MACPPSWR; + uint32_t RESERVED31[12]; + __IO uint32_t MACPOCR; + __IO uint32_t MACSPI0R; + __IO uint32_t MACSPI1R; + __IO uint32_t MACSPI2R; + __IO uint32_t MACLMIR; + uint32_t RESERVED32[11]; + __IO uint32_t MTLOMR; + uint32_t RESERVED33[7]; + __IO uint32_t MTLISR; + uint32_t RESERVED34[55]; + __IO uint32_t MTLTQOMR; + __IO uint32_t MTLTQUR; + __IO uint32_t MTLTQDR; + uint32_t RESERVED35[8]; + __IO uint32_t MTLQICSR; + __IO uint32_t MTLRQOMR; + __IO uint32_t MTLRQMPOCR; + __IO uint32_t MTLRQDR; + uint32_t RESERVED36[177]; + __IO uint32_t DMAMR; + __IO uint32_t DMASBMR; + __IO uint32_t DMAISR; + __IO uint32_t DMADSR; + uint32_t RESERVED37[60]; + __IO uint32_t DMACCR; + __IO uint32_t DMACTCR; + __IO uint32_t DMACRCR; + uint32_t RESERVED38[2]; + __IO uint32_t DMACTDLAR; + uint32_t RESERVED39; + __IO uint32_t DMACRDLAR; + __IO uint32_t DMACTDTPR; + uint32_t RESERVED40; + __IO uint32_t DMACRDTPR; + __IO uint32_t DMACTDRLR; + __IO uint32_t DMACRDRLR; + __IO uint32_t DMACIER; + __IO uint32_t DMACRIWTR; +__IO uint32_t DMACSFCSR; + uint32_t RESERVED41; + __IO uint32_t DMACCATDR; + uint32_t RESERVED42; + __IO uint32_t DMACCARDR; + uint32_t RESERVED43; + __IO uint32_t DMACCATBR; + uint32_t RESERVED44; + __IO uint32_t DMACCARBR; + __IO uint32_t DMACSR; +uint32_t RESERVED45[2]; +__IO uint32_t DMACMFCR; +}ETH_TypeDef; +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ +__IO uint32_t RTSR1; /*!< EXTI Rising trigger selection register, Address offset: 0x00 */ +__IO uint32_t FTSR1; /*!< EXTI Falling trigger selection register, Address offset: 0x04 */ +__IO uint32_t SWIER1; /*!< EXTI Software interrupt event register, Address offset: 0x08 */ +__IO uint32_t D3PMR1; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR1) Address offset: 0x0C */ +__IO uint32_t D3PCR1L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR1L) Address offset: 0x10 */ +__IO uint32_t D3PCR1H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR1H) Address offset: 0x14 */ +uint32_t RESERVED1[2]; /*!< Reserved, 0x18 to 0x1C */ +__IO uint32_t RTSR2; /*!< EXTI Rising trigger selection register, Address offset: 0x20 */ +__IO uint32_t FTSR2; /*!< EXTI Falling trigger selection register, Address offset: 0x24 */ +__IO uint32_t SWIER2; /*!< EXTI Software interrupt event register, Address offset: 0x28 */ +__IO uint32_t D3PMR2; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR2) Address offset: 0x2C */ +__IO uint32_t D3PCR2L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR2L) Address offset: 0x30 */ +__IO uint32_t D3PCR2H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR2H) Address offset: 0x34 */ +uint32_t RESERVED2[2]; /*!< Reserved, 0x38 to 0x3C */ +__IO uint32_t RTSR3; /*!< EXTI Rising trigger selection register, Address offset: 0x40 */ +__IO uint32_t FTSR3; /*!< EXTI Falling trigger selection register, Address offset: 0x44 */ +__IO uint32_t SWIER3; /*!< EXTI Software interrupt event register, Address offset: 0x48 */ +__IO uint32_t D3PMR3; /*!< EXTI D3 Pending mask register, (same register as to SRDPMR3) Address offset: 0x4C */ +__IO uint32_t D3PCR3L; /*!< EXTI D3 Pending clear selection register low, (same register as to SRDPCR3L) Address offset: 0x50 */ +__IO uint32_t D3PCR3H; /*!< EXTI D3 Pending clear selection register High, (same register as to SRDPCR3H) Address offset: 0x54 */ +uint32_t RESERVED3[10]; /*!< Reserved, 0x58 to 0x7C */ +__IO uint32_t IMR1; /*!< EXTI Interrupt mask register, Address offset: 0x80 */ +__IO uint32_t EMR1; /*!< EXTI Event mask register, Address offset: 0x84 */ +__IO uint32_t PR1; /*!< EXTI Pending register, Address offset: 0x88 */ +uint32_t RESERVED4; /*!< Reserved, 0x8C */ +__IO uint32_t IMR2; /*!< EXTI Interrupt mask register, Address offset: 0x90 */ +__IO uint32_t EMR2; /*!< EXTI Event mask register, Address offset: 0x94 */ +__IO uint32_t PR2; /*!< EXTI Pending register, Address offset: 0x98 */ +uint32_t RESERVED5; /*!< Reserved, 0x9C */ +__IO uint32_t IMR3; /*!< EXTI Interrupt mask register, Address offset: 0xA0 */ +__IO uint32_t EMR3; /*!< EXTI Event mask register, Address offset: 0xA4 */ +__IO uint32_t PR3; /*!< EXTI Pending register, Address offset: 0xA8 */ +}EXTI_TypeDef; + +/** + * @brief This structure registers corresponds to EXTI_Typdef CPU1/CPU2 registers subset (IMRx, EMRx and PRx), allowing to define EXTI_D1/EXTI_D2 + * with rapid/common access to these IMRx, EMRx, PRx registers for CPU1 and CPU2. + * Note that EXTI_D1 and EXTI_D2 bases addresses are calculated to point to CPUx first register: + * IMR1 in case of EXTI_D1 that is addressing CPU1 (Coretx-M7) + * C2IMR1 in case of EXTI_D2 that is addressing CPU2 (Coretx-M4) + * Note: EXTI_D2 and corresponding C2IMRx, C2EMRx and C2PRx registers are available for Dual Core devices only + */ + +typedef struct +{ +__IO uint32_t IMR1; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ +__IO uint32_t EMR1; /*!< EXTI Event mask register, Address offset: 0x04 */ +__IO uint32_t PR1; /*!< EXTI Pending register, Address offset: 0x08 */ +uint32_t RESERVED1; /*!< Reserved, 0x0C */ +__IO uint32_t IMR2; /*!< EXTI Interrupt mask register, Address offset: 0x10 */ +__IO uint32_t EMR2; /*!< EXTI Event mask register, Address offset: 0x14 */ +__IO uint32_t PR2; /*!< EXTI Pending register, Address offset: 0x18 */ +uint32_t RESERVED2; /*!< Reserved, 0x1C */ +__IO uint32_t IMR3; /*!< EXTI Interrupt mask register, Address offset: 0x20 */ +__IO uint32_t EMR3; /*!< EXTI Event mask register, Address offset: 0x24 */ +__IO uint32_t PR3; /*!< EXTI Pending register, Address offset: 0x28 */ +}EXTI_Core_TypeDef; + + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR1; /*!< Flash Key Register for bank1, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< Flash Option Key Register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< Flash Control Register for bank1, Address offset: 0x0C */ + __IO uint32_t SR1; /*!< Flash Status Register for bank1, Address offset: 0x10 */ + __IO uint32_t CCR1; /*!< Flash Control Register for bank1, Address offset: 0x14 */ + __IO uint32_t OPTCR; /*!< Flash Option Control Register, Address offset: 0x18 */ + __IO uint32_t OPTSR_CUR; /*!< Flash Option Status Current Register, Address offset: 0x1C */ + __IO uint32_t OPTSR_PRG; /*!< Flash Option Status to Program Register, Address offset: 0x20 */ + __IO uint32_t OPTCCR; /*!< Flash Option Clear Control Register, Address offset: 0x24 */ + __IO uint32_t PRAR_CUR1; /*!< Flash Current Protection Address Register for bank1, Address offset: 0x28 */ + __IO uint32_t PRAR_PRG1; /*!< Flash Protection Address to Program Register for bank1, Address offset: 0x2C */ + __IO uint32_t SCAR_CUR1; /*!< Flash Current Secure Address Register for bank1, Address offset: 0x30 */ + __IO uint32_t SCAR_PRG1; /*!< Flash Secure Address to Program Register for bank1, Address offset: 0x34 */ + __IO uint32_t WPSN_CUR1; /*!< Flash Current Write Protection Register on bank1, Address offset: 0x38 */ + __IO uint32_t WPSN_PRG1; /*!< Flash Write Protection to Program Register on bank1, Address offset: 0x3C */ + __IO uint32_t BOOT_CUR; /*!< Flash Current Boot Address for Pelican Core Register, Address offset: 0x40 */ + __IO uint32_t BOOT_PRG; /*!< Flash Boot Address to Program for Pelican Core Register, Address offset: 0x44 */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x48 to 0x4C */ + __IO uint32_t CRCCR1; /*!< Flash CRC Control register For Bank1 Register , Address offset: 0x50 */ + __IO uint32_t CRCSADD1; /*!< Flash CRC Start Address Register for Bank1 , Address offset: 0x54 */ + __IO uint32_t CRCEADD1; /*!< Flash CRC End Address Register for Bank1 , Address offset: 0x58 */ + __IO uint32_t CRCDATA; /*!< Flash CRC Data Register for Bank1 , Address offset: 0x5C */ + __IO uint32_t ECC_FA1; /*!< Flash ECC Fail Address For Bank1 Register , Address offset: 0x60 */ + uint32_t RESERVED[3]; /*!< Reserved, 0x64 to 0x6C */ + __IO uint32_t OPTSR2_CUR; /*!< Flash Option Status Current Register 2, Address offset: 0x70 */ + __IO uint32_t OPTSR2_PRG; /*!< Flash Option Status to Program Register 2, Address offset: 0x74 */ +} FLASH_TypeDef; + +/** + * @brief Filter and Mathematical ACcelerator + */ +typedef struct +{ + __IO uint32_t X1BUFCFG; /*!< FMAC X1 Buffer Configuration register, Address offset: 0x00 */ + __IO uint32_t X2BUFCFG; /*!< FMAC X2 Buffer Configuration register, Address offset: 0x04 */ + __IO uint32_t YBUFCFG; /*!< FMAC Y Buffer Configuration register, Address offset: 0x08 */ + __IO uint32_t PARAM; /*!< FMAC Parameter register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FMAC Control register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< FMAC Status register, Address offset: 0x14 */ + __IO uint32_t WDATA; /*!< FMAC Write Data register, Address offset: 0x18 */ + __IO uint32_t RDATA; /*!< FMAC Read Data register, Address offset: 0x1C */ +} FMAC_TypeDef; + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank2 + */ + +typedef struct +{ + __IO uint32_t PCR2; /*!< NAND Flash control register 2, Address offset: 0x60 */ + __IO uint32_t SR2; /*!< NAND Flash FIFO status and interrupt register 2, Address offset: 0x64 */ + __IO uint32_t PMEM2; /*!< NAND Flash Common memory space timing register 2, Address offset: 0x68 */ + __IO uint32_t PATT2; /*!< NAND Flash Attribute memory space timing register 2, Address offset: 0x6C */ + uint32_t RESERVED0; /*!< Reserved, 0x70 */ + __IO uint32_t ECCR2; /*!< NAND Flash ECC result registers 2, Address offset: 0x74 */ +} FMC_Bank2_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register 3, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register 3, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register 3, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register 3, Address offset: 0x8C */ + uint32_t RESERVED; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers 3, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief Flexible Memory Controller Bank5 and 6 + */ + + +typedef struct +{ + __IO uint32_t SDCR[2]; /*!< SDRAM Control registers , Address offset: 0x140-0x144 */ + __IO uint32_t SDTR[2]; /*!< SDRAM Timing registers , Address offset: 0x148-0x14C */ + __IO uint32_t SDCMR; /*!< SDRAM Command Mode register, Address offset: 0x150 */ + __IO uint32_t SDRTR; /*!< SDRAM Refresh Timer register, Address offset: 0x154 */ + __IO uint32_t SDSR; /*!< SDRAM Status register, Address offset: 0x158 */ +} FMC_Bank5_6_TypeDef; + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief Operational Amplifier (OPAMP) + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< OPAMP control/status register, Address offset: 0x00 */ + __IO uint32_t OTR; /*!< OPAMP offset trimming register for normal mode, Address offset: 0x04 */ + __IO uint32_t HSOTR; /*!< OPAMP offset trimming register for high speed mode, Address offset: 0x08 */ +} OPAMP_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x00 */ + __IO uint32_t PMCR; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + __IO uint32_t CFGR; /*!< SYSCFG configuration registers, Address offset: 0x18 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t CCCSR; /*!< SYSCFG compensation cell control/status register, Address offset: 0x20 */ + __IO uint32_t CCVR; /*!< SYSCFG compensation cell value register, Address offset: 0x24 */ + __IO uint32_t CCCR; /*!< SYSCFG compensation cell code register, Address offset: 0x28 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x2C */ + __IO uint32_t ADC2ALT; /*!< ADC2 internal input alternate connection register, Address offset: 0x30 */ + uint32_t RESERVED4[60]; /*!< Reserved, 0x34-0x120 */ + __IO uint32_t PKGR; /*!< SYSCFG package register, Address offset: 0x124 */ + uint32_t RESERVED5[118]; /*!< Reserved, 0x128-0x2FC */ + __IO uint32_t UR0; /*!< SYSCFG user register 0, Address offset: 0x300 */ + __IO uint32_t UR1; /*!< SYSCFG user register 1, Address offset: 0x304 */ + __IO uint32_t UR2; /*!< SYSCFG user register 2, Address offset: 0x308 */ + __IO uint32_t UR3; /*!< SYSCFG user register 3, Address offset: 0x30C */ + __IO uint32_t UR4; /*!< SYSCFG user register 4, Address offset: 0x310 */ + __IO uint32_t UR5; /*!< SYSCFG user register 5, Address offset: 0x314 */ + __IO uint32_t UR6; /*!< SYSCFG user register 6, Address offset: 0x318 */ + __IO uint32_t UR7; /*!< SYSCFG user register 7, Address offset: 0x31C */ + uint32_t RESERVED6[3]; /*!< Reserved, Address offset: 0x320-0x328 */ + __IO uint32_t UR11; /*!< SYSCFG user register 11, Address offset: 0x32C */ + __IO uint32_t UR12; /*!< SYSCFG user register 12, Address offset: 0x330 */ + __IO uint32_t UR13; /*!< SYSCFG user register 13, Address offset: 0x334 */ + __IO uint32_t UR14; /*!< SYSCFG user register 14, Address offset: 0x338 */ + __IO uint32_t UR15; /*!< SYSCFG user register 15, Address offset: 0x33C */ + __IO uint32_t UR16; /*!< SYSCFG user register 16, Address offset: 0x340 */ + __IO uint32_t UR17; /*!< SYSCFG user register 17, Address offset: 0x344 */ + __IO uint32_t UR18; /*!< SYSCFG user register 18, Address offset: 0x348 */ + +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + + +/** + * @brief LCD-TFT Display Controller + */ + +typedef struct +{ + uint32_t RESERVED0[2]; /*!< Reserved, 0x00-0x04 */ + __IO uint32_t SSCR; /*!< LTDC Synchronization Size Configuration Register, Address offset: 0x08 */ + __IO uint32_t BPCR; /*!< LTDC Back Porch Configuration Register, Address offset: 0x0C */ + __IO uint32_t AWCR; /*!< LTDC Active Width Configuration Register, Address offset: 0x10 */ + __IO uint32_t TWCR; /*!< LTDC Total Width Configuration Register, Address offset: 0x14 */ + __IO uint32_t GCR; /*!< LTDC Global Control Register, Address offset: 0x18 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x1C-0x20 */ + __IO uint32_t SRCR; /*!< LTDC Shadow Reload Configuration Register, Address offset: 0x24 */ + uint32_t RESERVED2[1]; /*!< Reserved, 0x28 */ + __IO uint32_t BCCR; /*!< LTDC Background Color Configuration Register, Address offset: 0x2C */ + uint32_t RESERVED3[1]; /*!< Reserved, 0x30 */ + __IO uint32_t IER; /*!< LTDC Interrupt Enable Register, Address offset: 0x34 */ + __IO uint32_t ISR; /*!< LTDC Interrupt Status Register, Address offset: 0x38 */ + __IO uint32_t ICR; /*!< LTDC Interrupt Clear Register, Address offset: 0x3C */ + __IO uint32_t LIPCR; /*!< LTDC Line Interrupt Position Configuration Register, Address offset: 0x40 */ + __IO uint32_t CPSR; /*!< LTDC Current Position Status Register, Address offset: 0x44 */ + __IO uint32_t CDSR; /*!< LTDC Current Display Status Register, Address offset: 0x48 */ +} LTDC_TypeDef; + +/** + * @brief LCD-TFT Display layer x Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< LTDC Layerx Control Register Address offset: 0x84 */ + __IO uint32_t WHPCR; /*!< LTDC Layerx Window Horizontal Position Configuration Register Address offset: 0x88 */ + __IO uint32_t WVPCR; /*!< LTDC Layerx Window Vertical Position Configuration Register Address offset: 0x8C */ + __IO uint32_t CKCR; /*!< LTDC Layerx Color Keying Configuration Register Address offset: 0x90 */ + __IO uint32_t PFCR; /*!< LTDC Layerx Pixel Format Configuration Register Address offset: 0x94 */ + __IO uint32_t CACR; /*!< LTDC Layerx Constant Alpha Configuration Register Address offset: 0x98 */ + __IO uint32_t DCCR; /*!< LTDC Layerx Default Color Configuration Register Address offset: 0x9C */ + __IO uint32_t BFCR; /*!< LTDC Layerx Blending Factors Configuration Register Address offset: 0xA0 */ + uint32_t RESERVED0[2]; /*!< Reserved */ + __IO uint32_t CFBAR; /*!< LTDC Layerx Color Frame Buffer Address Register Address offset: 0xAC */ + __IO uint32_t CFBLR; /*!< LTDC Layerx Color Frame Buffer Length Register Address offset: 0xB0 */ + __IO uint32_t CFBLNR; /*!< LTDC Layerx ColorFrame Buffer Line Number Register Address offset: 0xB4 */ + uint32_t RESERVED1[3]; /*!< Reserved */ + __IO uint32_t CLUTWR; /*!< LTDC Layerx CLUT Write Register Address offset: 0x144 */ + +} LTDC_Layer_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CSR1; /*!< PWR power control status register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x08 */ + __IO uint32_t CR3; /*!< PWR power control register 3, Address offset: 0x0C */ + __IO uint32_t CPUCR; /*!< PWR CPU control register, Address offset: 0x10 */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t D3CR; /*!< PWR D3 domain control register, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t WKUPCR; /*!< PWR wakeup clear register, Address offset: 0x20 */ + __IO uint32_t WKUPFR; /*!< PWR wakeup flag register, Address offset: 0x24 */ + __IO uint32_t WKUPEPR; /*!< PWR wakeup enable and polarity register, Address offset: 0x28 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t HSICFGR; /*!< HSI Clock Calibration Register, Address offset: 0x04 */ + __IO uint32_t CRRCR; /*!< Clock Recovery RC Register, Address offset: 0x08 */ + __IO uint32_t CSICFGR; /*!< CSI Clock Calibration Register, Address offset: 0x0C */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x10 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t D1CFGR; /*!< RCC Domain 1 configuration register, Address offset: 0x18 */ + __IO uint32_t D2CFGR; /*!< RCC Domain 2 configuration register, Address offset: 0x1C */ + __IO uint32_t D3CFGR; /*!< RCC Domain 3 configuration register, Address offset: 0x20 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x24 */ + __IO uint32_t PLLCKSELR; /*!< RCC PLLs Clock Source Selection Register, Address offset: 0x28 */ + __IO uint32_t PLLCFGR; /*!< RCC PLLs Configuration Register, Address offset: 0x2C */ + __IO uint32_t PLL1DIVR; /*!< RCC PLL1 Dividers Configuration Register, Address offset: 0x30 */ + __IO uint32_t PLL1FRACR; /*!< RCC PLL1 Fractional Divider Configuration Register, Address offset: 0x34 */ + __IO uint32_t PLL2DIVR; /*!< RCC PLL2 Dividers Configuration Register, Address offset: 0x38 */ + __IO uint32_t PLL2FRACR; /*!< RCC PLL2 Fractional Divider Configuration Register, Address offset: 0x3C */ + __IO uint32_t PLL3DIVR; /*!< RCC PLL3 Dividers Configuration Register, Address offset: 0x40 */ + __IO uint32_t PLL3FRACR; /*!< RCC PLL3 Fractional Divider Configuration Register, Address offset: 0x44 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x48 */ + __IO uint32_t D1CCIPR; /*!< RCC Domain 1 Kernel Clock Configuration Register Address offset: 0x4C */ + __IO uint32_t D2CCIP1R; /*!< RCC Domain 2 Kernel Clock Configuration Register Address offset: 0x50 */ + __IO uint32_t D2CCIP2R; /*!< RCC Domain 2 Kernel Clock Configuration Register Address offset: 0x54 */ + __IO uint32_t D3CCIPR; /*!< RCC Domain 3 Kernel Clock Configuration Register Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x5C */ + __IO uint32_t CIER; /*!< RCC Clock Source Interrupt Enable Register Address offset: 0x60 */ + __IO uint32_t CIFR; /*!< RCC Clock Source Interrupt Flag Register Address offset: 0x64 */ + __IO uint32_t CICR; /*!< RCC Clock Source Interrupt Clear Register Address offset: 0x68 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x6C */ + __IO uint32_t BDCR; /*!< RCC Vswitch Backup Domain Control Register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x78 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x7C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x80 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x84 */ + __IO uint32_t AHB4RSTR; /*!< RCC AHB4 peripheral reset register, Address offset: 0x88 */ + __IO uint32_t APB3RSTR; /*!< RCC APB3 peripheral reset register, Address offset: 0x8C */ + __IO uint32_t APB1LRSTR; /*!< RCC APB1 peripheral reset Low Word register, Address offset: 0x90 */ + __IO uint32_t APB1HRSTR; /*!< RCC APB1 peripheral reset High Word register, Address offset: 0x94 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x98 */ + __IO uint32_t APB4RSTR; /*!< RCC APB4 peripheral reset register, Address offset: 0x9C */ + __IO uint32_t GCR; /*!< RCC RCC Global Control Register, Address offset: 0xA0 */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0xA4 */ + __IO uint32_t D3AMR; /*!< RCC Domain 3 Autonomous Mode Register, Address offset: 0xA8 */ + uint32_t RESERVED11[9]; /*!< Reserved, 0xAC-0xCC Address offset: 0xAC */ + __IO uint32_t RSR; /*!< RCC Reset status register, Address offset: 0xD0 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0xD4 */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0xD8 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0xDC */ + __IO uint32_t AHB4ENR; /*!< RCC AHB4 peripheral clock register, Address offset: 0xE0 */ + __IO uint32_t APB3ENR; /*!< RCC APB3 peripheral clock register, Address offset: 0xE4 */ + __IO uint32_t APB1LENR; /*!< RCC APB1 peripheral clock Low Word register, Address offset: 0xE8 */ + __IO uint32_t APB1HENR; /*!< RCC APB1 peripheral clock High Word register, Address offset: 0xEC */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock register, Address offset: 0xF0 */ + __IO uint32_t APB4ENR; /*!< RCC APB4 peripheral clock register, Address offset: 0xF4 */ + uint32_t RESERVED12; /*!< Reserved, Address offset: 0xF8 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral sleep clock register, Address offset: 0xFC */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral sleep clock register, Address offset: 0x100 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral sleep clock register, Address offset: 0x104 */ + __IO uint32_t AHB4LPENR; /*!< RCC AHB4 peripheral sleep clock register, Address offset: 0x108 */ + __IO uint32_t APB3LPENR; /*!< RCC APB3 peripheral sleep clock register, Address offset: 0x10C */ + __IO uint32_t APB1LLPENR; /*!< RCC APB1 peripheral sleep clock Low Word register, Address offset: 0x110 */ + __IO uint32_t APB1HLPENR; /*!< RCC APB1 peripheral sleep clock High Word register, Address offset: 0x114 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral sleep clock register, Address offset: 0x118 */ + __IO uint32_t APB4LPENR; /*!< RCC APB4 peripheral sleep clock register, Address offset: 0x11C */ + uint32_t RESERVED13[4]; /*!< Reserved, 0x120-0x12C Address offset: 0x120 */ + +} RCC_TypeDef; + + +/** + * @brief Real-Time Clock + */ +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAMPCR; /*!< RTC tamper configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x48 */ + __IO uint32_t OR; /*!< RTC option register, Address offset: 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 0, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ + __IO uint32_t BKP20R; /*!< RTC backup register 20, Address offset: 0xA0 */ + __IO uint32_t BKP21R; /*!< RTC backup register 21, Address offset: 0xA4 */ + __IO uint32_t BKP22R; /*!< RTC backup register 22, Address offset: 0xA8 */ + __IO uint32_t BKP23R; /*!< RTC backup register 23, Address offset: 0xAC */ + __IO uint32_t BKP24R; /*!< RTC backup register 24, Address offset: 0xB0 */ + __IO uint32_t BKP25R; /*!< RTC backup register 25, Address offset: 0xB4 */ + __IO uint32_t BKP26R; /*!< RTC backup register 26, Address offset: 0xB8 */ + __IO uint32_t BKP27R; /*!< RTC backup register 27, Address offset: 0xBC */ + __IO uint32_t BKP28R; /*!< RTC backup register 28, Address offset: 0xC0 */ + __IO uint32_t BKP29R; /*!< RTC backup register 29, Address offset: 0xC4 */ + __IO uint32_t BKP30R; /*!< RTC backup register 30, Address offset: 0xC8 */ + __IO uint32_t BKP31R; /*!< RTC backup register 31, Address offset: 0xCC */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ + uint32_t RESERVED0[16]; /*!< Reserved, 0x04 - 0x43 */ + __IO uint32_t PDMCR; /*!< SAI PDM control register, Address offset: 0x44 */ + __IO uint32_t PDMDLY; /*!< SAI PDM delay register, Address offset: 0x48 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SPDIF-RX Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< Control register, Address offset: 0x00 */ + __IO uint32_t IMR; /*!< Interrupt mask register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< Status register, Address offset: 0x08 */ + __IO uint32_t IFCR; /*!< Interrupt Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DR; /*!< Data input register, Address offset: 0x10 */ + __IO uint32_t CSR; /*!< Channel Status register, Address offset: 0x14 */ + __IO uint32_t DIR; /*!< Debug Information register, Address offset: 0x18 */ + uint32_t RESERVED2; /*!< Reserved, 0x1A */ +} SPDIFRX_TypeDef; + + +/** + * @brief Secure digital input/output Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDMMC power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDMMC clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDMMC argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDMMC command register, Address offset: 0x0C */ + __I uint32_t RESPCMD; /*!< SDMMC command response register, Address offset: 0x10 */ + __I uint32_t RESP1; /*!< SDMMC response 1 register, Address offset: 0x14 */ + __I uint32_t RESP2; /*!< SDMMC response 2 register, Address offset: 0x18 */ + __I uint32_t RESP3; /*!< SDMMC response 3 register, Address offset: 0x1C */ + __I uint32_t RESP4; /*!< SDMMC response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDMMC data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDMMC data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDMMC data control register, Address offset: 0x2C */ + __I uint32_t DCOUNT; /*!< SDMMC data counter register, Address offset: 0x30 */ + __I uint32_t STA; /*!< SDMMC status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDMMC interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDMMC mask register, Address offset: 0x3C */ + __IO uint32_t ACKTIME; /*!< SDMMC Acknowledgement timer register, Address offset: 0x40 */ + uint32_t RESERVED0[3]; /*!< Reserved, 0x44 - 0x4C - 0x4C */ + __IO uint32_t IDMACTRL; /*!< SDMMC DMA control register, Address offset: 0x50 */ + __IO uint32_t IDMABSIZE; /*!< SDMMC DMA buffer size register, Address offset: 0x54 */ + __IO uint32_t IDMABASE0; /*!< SDMMC DMA buffer 0 base address register, Address offset: 0x58 */ + __IO uint32_t IDMABASE1; /*!< SDMMC DMA buffer 1 base address register, Address offset: 0x5C */ + uint32_t RESERVED1[8]; /*!< Reserved, 0x60-0x7C */ + __IO uint32_t FIFO; /*!< SDMMC data FIFO register, Address offset: 0x80 */ + uint32_t RESERVED2[222]; /*!< Reserved, 0x84-0x3F8 */ + __IO uint32_t IPVR; /*!< SDMMC data FIFO register, Address offset: 0x3FC */ +} SDMMC_TypeDef; + + +/** + * @brief Delay Block DLYB + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DELAY BLOCK control register, Address offset: 0x00 */ + __IO uint32_t CFGR; /*!< DELAY BLOCK configuration register, Address offset: 0x04 */ +} DLYB_TypeDef; + +/** + * @brief HW Semaphore HSEM + */ + +typedef struct +{ + __IO uint32_t R[32]; /*!< 2-step write lock and read back registers, Address offset: 00h-7Ch */ + __IO uint32_t RLR[32]; /*!< 1-step read lock registers, Address offset: 80h-FCh */ + __IO uint32_t C1IER; /*!< HSEM Interrupt enable register , Address offset: 100h */ + __IO uint32_t C1ICR; /*!< HSEM Interrupt clear register , Address offset: 104h */ + __IO uint32_t C1ISR; /*!< HSEM Interrupt Status register , Address offset: 108h */ + __IO uint32_t C1MISR; /*!< HSEM Interrupt Masked Status register , Address offset: 10Ch */ + uint32_t Reserved[12]; /* Reserved Address offset: 110h-13Ch */ + __IO uint32_t CR; /*!< HSEM Semaphore clear register , Address offset: 140h */ + __IO uint32_t KEYR; /*!< HSEM Semaphore clear key register , Address offset: 144h */ + +} HSEM_TypeDef; + +typedef struct +{ + __IO uint32_t IER; /*!< HSEM interrupt enable register , Address offset: 0h */ + __IO uint32_t ICR; /*!< HSEM interrupt clear register , Address offset: 4h */ + __IO uint32_t ISR; /*!< HSEM interrupt status register , Address offset: 8h */ + __IO uint32_t MISR; /*!< HSEM masked interrupt status register , Address offset: Ch */ +} HSEM_Common_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI/I2S Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */ + __IO uint32_t CFG1; /*!< SPI Configuration register 1, Address offset: 0x08 */ + __IO uint32_t CFG2; /*!< SPI Configuration register 2, Address offset: 0x0C */ + __IO uint32_t IER; /*!< SPI/I2S Interrupt Enable register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< SPI/I2S Status register, Address offset: 0x14 */ + __IO uint32_t IFCR; /*!< SPI/I2S Interrupt/Status flags clear register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t TXDR; /*!< SPI/I2S Transmit data register, Address offset: 0x20 */ + uint32_t RESERVED1[3]; /*!< Reserved, 0x24-0x2C */ + __IO uint32_t RXDR; /*!< SPI/I2S Receive data register, Address offset: 0x30 */ + uint32_t RESERVED2[3]; /*!< Reserved, 0x34-0x3C */ + __IO uint32_t CRCPOLY; /*!< SPI CRC Polynomial register, Address offset: 0x40 */ + __IO uint32_t TXCRC; /*!< SPI Transmitter CRC register, Address offset: 0x44 */ + __IO uint32_t RXCRC; /*!< SPI Receiver CRC register, Address offset: 0x48 */ + __IO uint32_t UDRDR; /*!< SPI Underrun data register, Address offset: 0x4C */ + __IO uint32_t I2SCFGR; /*!< I2S Configuration register, Address offset: 0x50 */ + +} SPI_TypeDef; + +/** + * @brief DTS + */ +typedef struct +{ + __IO uint32_t CFGR1; /*!< DTS configuration register, Address offset: 0x00 */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x04 */ + __IO uint32_t T0VALR1; /*!< DTS T0 Value register, Address offset: 0x08 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x0C */ + __IO uint32_t RAMPVALR; /*!< DTS Ramp value register, Address offset: 0x10 */ + __IO uint32_t ITR1; /*!< DTS Interrupt threshold register, Address offset: 0x14 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x18 */ + __IO uint32_t DR; /*!< DTS data register, Address offset: 0x1C */ + __IO uint32_t SR; /*!< DTS status register Address offset: 0x20 */ + __IO uint32_t ITENR; /*!< DTS Interrupt enable register, Address offset: 0x24 */ + __IO uint32_t ICIFR; /*!< DTS Clear Interrupt flag register, Address offset: 0x28 */ + __IO uint32_t OR; /*!< DTS option register 1, Address offset: 0x2C */ +} +DTS_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + uint32_t RESERVED1; /*!< Reserved, 0x50 */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x54 */ + __IO uint32_t CCR5; /*!< TIM capture/compare register5, Address offset: 0x58 */ + __IO uint32_t CCR6; /*!< TIM capture/compare register6, Address offset: 0x5C */ + __IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */ + __IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */ + __IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x68 */ +} TIM_TypeDef; + +/** + * @brief LPTIMIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + uint32_t RESERVED1; /*!< Reserved, 0x20 */ + __IO uint32_t CFGR2; /*!< LPTIM Configuration register, Address offset: 0x24 */ +} LPTIM_TypeDef; + +/** + * @brief Comparator + */ +typedef struct +{ + __IO uint32_t SR; /*!< Comparator status register, Address offset: 0x00 */ + __IO uint32_t ICFR; /*!< Comparator interrupt clear flag register, Address offset: 0x04 */ + __IO uint32_t OR; /*!< Comparator option register, Address offset: 0x08 */ +} COMPOPT_TypeDef; + +typedef struct +{ + __IO uint32_t CFGR; /*!< Comparator configuration register , Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CFGR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ + __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ +} USART_TypeDef; + +/** + * @brief Single Wire Protocol Master Interface SPWMI + */ +typedef struct +{ + __IO uint32_t CR; /*!< SWPMI Configuration/Control register, Address offset: 0x00 */ + __IO uint32_t BRR; /*!< SWPMI bitrate register, Address offset: 0x04 */ + uint32_t RESERVED1; /*!< Reserved, 0x08 */ + __IO uint32_t ISR; /*!< SWPMI Interrupt and Status register, Address offset: 0x0C */ + __IO uint32_t ICR; /*!< SWPMI Interrupt Flag Clear register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< SWPMI Interrupt Enable register, Address offset: 0x14 */ + __IO uint32_t RFL; /*!< SWPMI Receive Frame Length register, Address offset: 0x18 */ + __IO uint32_t TDR; /*!< SWPMI Transmit data register, Address offset: 0x1C */ + __IO uint32_t RDR; /*!< SWPMI Receive data register, Address offset: 0x20 */ + __IO uint32_t OR; /*!< SWPMI Option register, Address offset: 0x24 */ +} SWPMI_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RAM_ECC_Specific_Registers + */ +typedef struct +{ + __IO uint32_t CR; /*!< RAMECC monitor configuration register */ + __IO uint32_t SR; /*!< RAMECC monitor status register */ + __IO uint32_t FAR; /*!< RAMECC monitor failing address register */ + __IO uint32_t FDRL; /*!< RAMECC monitor failing data low register */ + __IO uint32_t FDRH; /*!< RAMECC monitor failing data high register */ + __IO uint32_t FECR; /*!< RAMECC monitor failing ECC error code register */ +} RAMECC_MonitorTypeDef; + +typedef struct +{ + __IO uint32_t IER; /*!< RAMECC interrupt enable register */ +} RAMECC_TypeDef; +/** + * @} + */ + + +/** + * @brief Crypto Processor + */ + +typedef struct +{ + __IO uint32_t CR; /*!< CRYP control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< CRYP status register, Address offset: 0x04 */ + __IO uint32_t DIN; /*!< CRYP data input register, Address offset: 0x08 */ + __IO uint32_t DOUT; /*!< CRYP data output register, Address offset: 0x0C */ + __IO uint32_t DMACR; /*!< CRYP DMA control register, Address offset: 0x10 */ + __IO uint32_t IMSCR; /*!< CRYP interrupt mask set/clear register, Address offset: 0x14 */ + __IO uint32_t RISR; /*!< CRYP raw interrupt status register, Address offset: 0x18 */ + __IO uint32_t MISR; /*!< CRYP masked interrupt status register, Address offset: 0x1C */ + __IO uint32_t K0LR; /*!< CRYP key left register 0, Address offset: 0x20 */ + __IO uint32_t K0RR; /*!< CRYP key right register 0, Address offset: 0x24 */ + __IO uint32_t K1LR; /*!< CRYP key left register 1, Address offset: 0x28 */ + __IO uint32_t K1RR; /*!< CRYP key right register 1, Address offset: 0x2C */ + __IO uint32_t K2LR; /*!< CRYP key left register 2, Address offset: 0x30 */ + __IO uint32_t K2RR; /*!< CRYP key right register 2, Address offset: 0x34 */ + __IO uint32_t K3LR; /*!< CRYP key left register 3, Address offset: 0x38 */ + __IO uint32_t K3RR; /*!< CRYP key right register 3, Address offset: 0x3C */ + __IO uint32_t IV0LR; /*!< CRYP initialization vector left-word register 0, Address offset: 0x40 */ + __IO uint32_t IV0RR; /*!< CRYP initialization vector right-word register 0, Address offset: 0x44 */ + __IO uint32_t IV1LR; /*!< CRYP initialization vector left-word register 1, Address offset: 0x48 */ + __IO uint32_t IV1RR; /*!< CRYP initialization vector right-word register 1, Address offset: 0x4C */ + __IO uint32_t CSGCMCCM0R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 0, Address offset: 0x50 */ + __IO uint32_t CSGCMCCM1R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 1, Address offset: 0x54 */ + __IO uint32_t CSGCMCCM2R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 2, Address offset: 0x58 */ + __IO uint32_t CSGCMCCM3R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 3, Address offset: 0x5C */ + __IO uint32_t CSGCMCCM4R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 4, Address offset: 0x60 */ + __IO uint32_t CSGCMCCM5R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 5, Address offset: 0x64 */ + __IO uint32_t CSGCMCCM6R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 6, Address offset: 0x68 */ + __IO uint32_t CSGCMCCM7R; /*!< CRYP GCM/GMAC or CCM/CMAC context swap register 7, Address offset: 0x6C */ + __IO uint32_t CSGCM0R; /*!< CRYP GCM/GMAC context swap register 0, Address offset: 0x70 */ + __IO uint32_t CSGCM1R; /*!< CRYP GCM/GMAC context swap register 1, Address offset: 0x74 */ + __IO uint32_t CSGCM2R; /*!< CRYP GCM/GMAC context swap register 2, Address offset: 0x78 */ + __IO uint32_t CSGCM3R; /*!< CRYP GCM/GMAC context swap register 3, Address offset: 0x7C */ + __IO uint32_t CSGCM4R; /*!< CRYP GCM/GMAC context swap register 4, Address offset: 0x80 */ + __IO uint32_t CSGCM5R; /*!< CRYP GCM/GMAC context swap register 5, Address offset: 0x84 */ + __IO uint32_t CSGCM6R; /*!< CRYP GCM/GMAC context swap register 6, Address offset: 0x88 */ + __IO uint32_t CSGCM7R; /*!< CRYP GCM/GMAC context swap register 7, Address offset: 0x8C */ +} CRYP_TypeDef; + +/** + * @brief HASH + */ + +typedef struct +{ + __IO uint32_t CR; /*!< HASH control register, Address offset: 0x00 */ + __IO uint32_t DIN; /*!< HASH data input register, Address offset: 0x04 */ + __IO uint32_t STR; /*!< HASH start register, Address offset: 0x08 */ + __IO uint32_t HR[5]; /*!< HASH digest registers, Address offset: 0x0C-0x1C */ + __IO uint32_t IMR; /*!< HASH interrupt enable register, Address offset: 0x20 */ + __IO uint32_t SR; /*!< HASH status register, Address offset: 0x24 */ + uint32_t RESERVED[52]; /*!< Reserved, 0x28-0xF4 */ + __IO uint32_t CSR[54]; /*!< HASH context swap registers, Address offset: 0x0F8-0x1CC */ +} HASH_TypeDef; + +/** + * @brief HASH_DIGEST + */ + +typedef struct +{ + __IO uint32_t HR[8]; /*!< HASH digest registers, Address offset: 0x310-0x32C */ +} HASH_DIGEST_TypeDef; + + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ + uint32_t RESERVED; + __IO uint32_t HTCR; /*!< RNG health test configuration register, Address offset: 0x10 */ +} RNG_TypeDef; + +/** + * @brief MDIOS + */ + +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t WRFR; + __IO uint32_t CWRFR; + __IO uint32_t RDFR; + __IO uint32_t CRDFR; + __IO uint32_t SR; + __IO uint32_t CLRFR; + uint32_t RESERVED[57]; + __IO uint32_t DINR0; + __IO uint32_t DINR1; + __IO uint32_t DINR2; + __IO uint32_t DINR3; + __IO uint32_t DINR4; + __IO uint32_t DINR5; + __IO uint32_t DINR6; + __IO uint32_t DINR7; + __IO uint32_t DINR8; + __IO uint32_t DINR9; + __IO uint32_t DINR10; + __IO uint32_t DINR11; + __IO uint32_t DINR12; + __IO uint32_t DINR13; + __IO uint32_t DINR14; + __IO uint32_t DINR15; + __IO uint32_t DINR16; + __IO uint32_t DINR17; + __IO uint32_t DINR18; + __IO uint32_t DINR19; + __IO uint32_t DINR20; + __IO uint32_t DINR21; + __IO uint32_t DINR22; + __IO uint32_t DINR23; + __IO uint32_t DINR24; + __IO uint32_t DINR25; + __IO uint32_t DINR26; + __IO uint32_t DINR27; + __IO uint32_t DINR28; + __IO uint32_t DINR29; + __IO uint32_t DINR30; + __IO uint32_t DINR31; + __IO uint32_t DOUTR0; + __IO uint32_t DOUTR1; + __IO uint32_t DOUTR2; + __IO uint32_t DOUTR3; + __IO uint32_t DOUTR4; + __IO uint32_t DOUTR5; + __IO uint32_t DOUTR6; + __IO uint32_t DOUTR7; + __IO uint32_t DOUTR8; + __IO uint32_t DOUTR9; + __IO uint32_t DOUTR10; + __IO uint32_t DOUTR11; + __IO uint32_t DOUTR12; + __IO uint32_t DOUTR13; + __IO uint32_t DOUTR14; + __IO uint32_t DOUTR15; + __IO uint32_t DOUTR16; + __IO uint32_t DOUTR17; + __IO uint32_t DOUTR18; + __IO uint32_t DOUTR19; + __IO uint32_t DOUTR20; + __IO uint32_t DOUTR21; + __IO uint32_t DOUTR22; + __IO uint32_t DOUTR23; + __IO uint32_t DOUTR24; + __IO uint32_t DOUTR25; + __IO uint32_t DOUTR26; + __IO uint32_t DOUTR27; + __IO uint32_t DOUTR28; + __IO uint32_t DOUTR29; + __IO uint32_t DOUTR30; + __IO uint32_t DOUTR31; +} MDIOS_TypeDef; + + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + __IO uint32_t GSNPSID; /* USB_OTG core ID 040h*/ + __IO uint32_t GHWCFG1; /* User HW config1 044h*/ + __IO uint32_t GHWCFG2; /* User HW config2 048h*/ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + __IO uint32_t GPWRDN; /*!< Power Down Register 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + __IO uint32_t GADPCTL; /*!< ADP Timer, Control and Status Register 60Ch */ + uint32_t Reserved43[39]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; +/** + * @} + */ + +/** + * @brief OCTO Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< OCTOSPI Control register, Address offset: 0x000 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x004 */ + __IO uint32_t DCR1; /*!< OCTOSPI Device Configuration register 1, Address offset: 0x008 */ + __IO uint32_t DCR2; /*!< OCTOSPI Device Configuration register 2, Address offset: 0x00C */ + __IO uint32_t DCR3; /*!< OCTOSPI Device Configuration register 3, Address offset: 0x010 */ + __IO uint32_t DCR4; /*!< OCTOSPI Device Configuration register 4, Address offset: 0x014 */ + uint32_t RESERVED1[2]; /*!< Reserved, Address offset: 0x018-0x01C */ + __IO uint32_t SR; /*!< OCTOSPI Status register, Address offset: 0x020 */ + __IO uint32_t FCR; /*!< OCTOSPI Flag Clear register, Address offset: 0x024 */ + uint32_t RESERVED2[6]; /*!< Reserved, Address offset: 0x028-0x03C */ + __IO uint32_t DLR; /*!< OCTOSPI Data Length register, Address offset: 0x040 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x044 */ + __IO uint32_t AR; /*!< OCTOSPI Address register, Address offset: 0x048 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x04C */ + __IO uint32_t DR; /*!< OCTOSPI Data register, Address offset: 0x050 */ + uint32_t RESERVED5[11]; /*!< Reserved, Address offset: 0x054-0x07C */ + __IO uint32_t PSMKR; /*!< OCTOSPI Polling Status Mask register, Address offset: 0x080 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x084 */ + __IO uint32_t PSMAR; /*!< OCTOSPI Polling Status Match register, Address offset: 0x088 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x08C */ + __IO uint32_t PIR; /*!< OCTOSPI Polling Interval register, Address offset: 0x090 */ + uint32_t RESERVED8[27]; /*!< Reserved, Address offset: 0x094-0x0FC */ + __IO uint32_t CCR; /*!< OCTOSPI Communication Configuration register, Address offset: 0x100 */ + uint32_t RESERVED9; /*!< Reserved, Address offset: 0x104 */ + __IO uint32_t TCR; /*!< OCTOSPI Timing Configuration register, Address offset: 0x108 */ + uint32_t RESERVED10; /*!< Reserved, Address offset: 0x10C */ + __IO uint32_t IR; /*!< OCTOSPI Instruction register, Address offset: 0x110 */ + uint32_t RESERVED11[3]; /*!< Reserved, Address offset: 0x114-0x11C */ + __IO uint32_t ABR; /*!< OCTOSPI Alternate Bytes register, Address offset: 0x120 */ + uint32_t RESERVED12[3]; /*!< Reserved, Address offset: 0x124-0x12C */ + __IO uint32_t LPTR; /*!< OCTOSPI Low Power Timeout register, Address offset: 0x130 */ + uint32_t RESERVED13[3]; /*!< Reserved, Address offset: 0x134-0x13C */ + __IO uint32_t WPCCR; /*!< OCTOSPI Wrap Communication Configuration register, Address offset: 0x140 */ + uint32_t RESERVED14; /*!< Reserved, Address offset: 0x144 */ + __IO uint32_t WPTCR; /*!< OCTOSPI Wrap Timing Configuration register, Address offset: 0x148 */ + uint32_t RESERVED15; /*!< Reserved, Address offset: 0x14C */ + __IO uint32_t WPIR; /*!< OCTOSPI Wrap Instruction register, Address offset: 0x150 */ + uint32_t RESERVED16[3]; /*!< Reserved, Address offset: 0x154-0x15C */ + __IO uint32_t WPABR; /*!< OCTOSPI Wrap Alternate Bytes register, Address offset: 0x160 */ + uint32_t RESERVED17[7]; /*!< Reserved, Address offset: 0x164-0x17C */ + __IO uint32_t WCCR; /*!< OCTOSPI Write Communication Configuration register, Address offset: 0x180 */ + uint32_t RESERVED18; /*!< Reserved, Address offset: 0x184 */ + __IO uint32_t WTCR; /*!< OCTOSPI Write Timing Configuration register, Address offset: 0x188 */ + uint32_t RESERVED19; /*!< Reserved, Address offset: 0x18C */ + __IO uint32_t WIR; /*!< OCTOSPI Write Instruction register, Address offset: 0x190 */ + uint32_t RESERVED20[3]; /*!< Reserved, Address offset: 0x194-0x19C */ + __IO uint32_t WABR; /*!< OCTOSPI Write Alternate Bytes register, Address offset: 0x1A0 */ + uint32_t RESERVED21[23]; /*!< Reserved, Address offset: 0x1A4-0x1FC */ + __IO uint32_t HLCR; /*!< OCTOSPI Hyperbus Latency Configuration register, Address offset: 0x200 */ + uint32_t RESERVED22[122]; /*!< Reserved, Address offset: 0x204-0x3EC */ + __IO uint32_t HWCFGR; /*!< OCTOSPI HW Configuration register, Address offset: 0x3F0 */ + __IO uint32_t VER; /*!< OCTOSPI Version register, Address offset: 0x3F4 */ + __IO uint32_t ID; /*!< OCTOSPI Identification register, Address offset: 0x3F8 */ + __IO uint32_t MID; /*!< OCTOPSI HW Magic ID register, Address offset: 0x3FC */ +} OCTOSPI_TypeDef; + +/** + * @} + */ +/** + * @brief OCTO Serial Peripheral Interface IO Manager + */ + +typedef struct +{ + __IO uint32_t CR; /*!< OCTOSPI IO Manager Control register, Address offset: 0x00 */ + __IO uint32_t PCR[3]; /*!< OCTOSPI IO Manager Port[1:3] Configuration register, Address offset: 0x04-0x20 */ +} OCTOSPIM_TypeDef; + +/** + * @} + */ + +/** + * @brief OTFD register + */ +typedef struct +{ + __IO uint32_t REG_CONFIGR; + __IO uint32_t REG_START_ADDR; + __IO uint32_t REG_END_ADDR; + __IO uint32_t REG_NONCER0; + __IO uint32_t REG_NONCER1; + __IO uint32_t REG_KEYR0; + __IO uint32_t REG_KEYR1; + __IO uint32_t REG_KEYR2; + __IO uint32_t REG_KEYR3; +} OTFDEC_Region_TypeDef; + +typedef struct +{ + __IO uint32_t CR; + uint32_t RESERVED1[191]; + __IO uint32_t ISR; + __IO uint32_t ICR; + __IO uint32_t IER; + uint32_t RESERVED2[56]; + __IO uint32_t HWCFGR2; + __IO uint32_t HWCFGR1; + __IO uint32_t VERR; + __IO uint32_t IPIDR; + __IO uint32_t SIDR; +} OTFDEC_TypeDef; +/** + * @} + */ + +/** + * @brief Global Programmer View + */ + +typedef struct +{ + uint32_t RESERVED0[2036]; /*!< Reserved, Address offset: 0x00-0x1FCC */ + __IO uint32_t AXI_PERIPH_ID_4; /*!< AXI interconnect - peripheral ID4 register, Address offset: 0x1FD0 */ + uint32_t AXI_PERIPH_ID_5; /*!< Reserved, Address offset: 0x1FD4 */ + uint32_t AXI_PERIPH_ID_6; /*!< Reserved, Address offset: 0x1FD8 */ + uint32_t AXI_PERIPH_ID_7; /*!< Reserved, Address offset: 0x1FDC */ + __IO uint32_t AXI_PERIPH_ID_0; /*!< AXI interconnect - peripheral ID0 register, Address offset: 0x1FE0 */ + __IO uint32_t AXI_PERIPH_ID_1; /*!< AXI interconnect - peripheral ID1 register, Address offset: 0x1FE4 */ + __IO uint32_t AXI_PERIPH_ID_2; /*!< AXI interconnect - peripheral ID2 register, Address offset: 0x1FE8 */ + __IO uint32_t AXI_PERIPH_ID_3; /*!< AXI interconnect - peripheral ID3 register, Address offset: 0x1FEC */ + __IO uint32_t AXI_COMP_ID_0; /*!< AXI interconnect - component ID0 register, Address offset: 0x1FF0 */ + __IO uint32_t AXI_COMP_ID_1; /*!< AXI interconnect - component ID1 register, Address offset: 0x1FF4 */ + __IO uint32_t AXI_COMP_ID_2; /*!< AXI interconnect - component ID2 register, Address offset: 0x1FF8 */ + __IO uint32_t AXI_COMP_ID_3; /*!< AXI interconnect - component ID3 register, Address offset: 0x1FFC */ + uint32_t RESERVED1[2]; /*!< Reserved, Address offset: 0x2000-0x2004 */ + __IO uint32_t AXI_TARG1_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 1 bus matrix issuing functionality register, Address offset: 0x2008 */ + uint32_t RESERVED2[6]; /*!< Reserved, Address offset: 0x200C-0x2020 */ + __IO uint32_t AXI_TARG1_FN_MOD2; /*!< AXI interconnect - TARG 1 bus matrix functionality 2 register, Address offset: 0x2024 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x2028 */ + __IO uint32_t AXI_TARG1_FN_MOD_LB; /*!< AXI interconnect - TARG 1 long burst functionality modification register, Address offset: 0x202C */ + uint32_t RESERVED4[54]; /*!< Reserved, Address offset: 0x2030-0x2104 */ + __IO uint32_t AXI_TARG1_FN_MOD; /*!< AXI interconnect - TARG 1 issuing functionality modification register, Address offset: 0x2108 */ + uint32_t RESERVED5[959]; /*!< Reserved, Address offset: 0x210C-0x3004 */ + __IO uint32_t AXI_TARG2_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 2 bus matrix issuing functionality register, Address offset: 0x3008 */ + uint32_t RESERVED6[6]; /*!< Reserved, Address offset: 0x300C-0x3020 */ + __IO uint32_t AXI_TARG2_FN_MOD2; /*!< AXI interconnect - TARG 2 bus matrix functionality 2 register, Address offset: 0x3024 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x3028 */ + __IO uint32_t AXI_TARG2_FN_MOD_LB; /*!< AXI interconnect - TARG 2 long burst functionality modification register, Address offset: 0x302C */ + uint32_t RESERVED8[54]; /*!< Reserved, Address offset: 0x3030-0x3104 */ + __IO uint32_t AXI_TARG2_FN_MOD; /*!< AXI interconnect - TARG 2 issuing functionality modification register, Address offset: 0x3108 */ + uint32_t RESERVED9[959]; /*!< Reserved, Address offset: 0x310C-0x4004 */ + __IO uint32_t AXI_TARG3_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 3 bus matrix issuing functionality register, Address offset: 0x4008 */ + uint32_t RESERVED10[1023]; /*!< Reserved, Address offset: 0x400C-0x5004 */ + __IO uint32_t AXI_TARG4_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 4 bus matrix issuing functionality register, Address offset: 0x5008 */ + uint32_t RESERVED11[1023]; /*!< Reserved, Address offset: 0x500C-0x6004 */ + __IO uint32_t AXI_TARG5_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 5 bus matrix issuing functionality register, Address offset: 0x6008 */ + uint32_t RESERVED12[1023]; /*!< Reserved, Address offset: 0x600C-0x7004 */ + __IO uint32_t AXI_TARG6_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 6 bus matrix issuing functionality register, Address offset: 0x7008 */ + uint32_t RESERVED13[1023]; /*!< Reserved, Address offset: 0x700C-0x8004 */ + __IO uint32_t AXI_TARG7_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 7 bus matrix issuing functionality register, Address offset: 0x8008 */ + uint32_t RESERVED14[6]; /*!< Reserved, Address offset: 0x800C-0x8020 */ + __IO uint32_t AXI_TARG7_FN_MOD2; /*!< AXI interconnect - TARG 7 bus matrix functionality 2 register, Address offset: 0x8024 */ + uint32_t RESERVED15; /*!< Reserved, Address offset: 0x8028 */ + __IO uint32_t AXI_TARG7_FN_MOD_LB; /*!< AXI interconnect - TARG 7 long burst functionality modification register, Address offset: 0x802C */ + uint32_t RESERVED16[54]; /*!< Reserved, Address offset: 0x8030-0x8104 */ + __IO uint32_t AXI_TARG7_FN_MOD; /*!< AXI interconnect - TARG 7 issuing functionality modification register, Address offset: 0x8108 */ + uint32_t RESERVED17[959]; /*!< Reserved, Address offset: 0x810C-0x9004 */ + __IO uint32_t AXI_TARG8_FN_MOD_ISS_BM; /*!< AXI interconnect - TARG 8 bus matrix issuing functionality register, Address offset: 0x9008 */ + uint32_t RESERVED117[6]; /*!< Reserved, Address offset: 0x900C-0x9020 */ + __IO uint32_t AXI_TARG8_FN_MOD2; /*!< AXI interconnect - TARG 8 bus matrix functionality 2 register, Address offset: 0x9024 */ + uint32_t RESERVED118[56]; /*!< Reserved, Address offset: 0x9028-0x9104 */ + __IO uint32_t AXI_TARG8_FN_MOD; /*!< AXI interconnect - TARG 8 issuing functionality modification register, Address offset: 0x9108 */ + uint32_t RESERVED119[58310]; /*!< Reserved, Address offset: 0x910C-0x42020 */ + __IO uint32_t AXI_INI1_FN_MOD2; /*!< AXI interconnect - INI 1 functionality modification 2 register, Address offset: 0x42024 */ + __IO uint32_t AXI_INI1_FN_MOD_AHB; /*!< AXI interconnect - INI 1 AHB functionality modification register, Address offset: 0x42028 */ + uint32_t RESERVED18[53]; /*!< Reserved, Address offset: 0x4202C-0x420FC */ + __IO uint32_t AXI_INI1_READ_QOS; /*!< AXI interconnect - INI 1 read QoS register, Address offset: 0x42100 */ + __IO uint32_t AXI_INI1_WRITE_QOS; /*!< AXI interconnect - INI 1 write QoS register, Address offset: 0x42104 */ + __IO uint32_t AXI_INI1_FN_MOD; /*!< AXI interconnect - INI 1 issuing functionality modification register, Address offset: 0x42108 */ + uint32_t RESERVED19[1021]; /*!< Reserved, Address offset: 0x4210C-0x430FC */ + __IO uint32_t AXI_INI2_READ_QOS; /*!< AXI interconnect - INI 2 read QoS register, Address offset: 0x43100 */ + __IO uint32_t AXI_INI2_WRITE_QOS; /*!< AXI interconnect - INI 2 write QoS register, Address offset: 0x43104 */ + __IO uint32_t AXI_INI2_FN_MOD; /*!< AXI interconnect - INI 2 issuing functionality modification register, Address offset: 0x43108 */ + uint32_t RESERVED20[966]; /*!< Reserved, Address offset: 0x4310C-0x44020 */ + __IO uint32_t AXI_INI3_FN_MOD2; /*!< AXI interconnect - INI 3 functionality modification 2 register, Address offset: 0x44024 */ + __IO uint32_t AXI_INI3_FN_MOD_AHB; /*!< AXI interconnect - INI 3 AHB functionality modification register, Address offset: 0x44028 */ + uint32_t RESERVED21[53]; /*!< Reserved, Address offset: 0x4402C-0x440FC */ + __IO uint32_t AXI_INI3_READ_QOS; /*!< AXI interconnect - INI 3 read QoS register, Address offset: 0x44100 */ + __IO uint32_t AXI_INI3_WRITE_QOS; /*!< AXI interconnect - INI 3 write QoS register, Address offset: 0x44104 */ + __IO uint32_t AXI_INI3_FN_MOD; /*!< AXI interconnect - INI 3 issuing functionality modification register, Address offset: 0x44108 */ + uint32_t RESERVED22[1021]; /*!< Reserved, Address offset: 0x4410C-0x450FC */ + __IO uint32_t AXI_INI4_READ_QOS; /*!< AXI interconnect - INI 4 read QoS register, Address offset: 0x45100 */ + __IO uint32_t AXI_INI4_WRITE_QOS; /*!< AXI interconnect - INI 4 write QoS register, Address offset: 0x45104 */ + __IO uint32_t AXI_INI4_FN_MOD; /*!< AXI interconnect - INI 4 issuing functionality modification register, Address offset: 0x45108 */ + uint32_t RESERVED23[1021]; /*!< Reserved, Address offset: 0x4510C-0x460FC */ + __IO uint32_t AXI_INI5_READ_QOS; /*!< AXI interconnect - INI 5 read QoS register, Address offset: 0x46100 */ + __IO uint32_t AXI_INI5_WRITE_QOS; /*!< AXI interconnect - INI 5 write QoS register, Address offset: 0x46104 */ + __IO uint32_t AXI_INI5_FN_MOD; /*!< AXI interconnect - INI 5 issuing functionality modification register, Address offset: 0x46108 */ + uint32_t RESERVED24[1021]; /*!< Reserved, Address offset: 0x4610C-0x470FC */ + __IO uint32_t AXI_INI6_READ_QOS; /*!< AXI interconnect - INI 6 read QoS register, Address offset: 0x47100 */ + __IO uint32_t AXI_INI6_WRITE_QOS; /*!< AXI interconnect - INI 6 write QoS register, Address offset: 0x47104 */ + __IO uint32_t AXI_INI6_FN_MOD; /*!< AXI interconnect - INI 6 issuing functionality modification register, Address offset: 0x47108 */ + +} GPV_TypeDef; + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define D1_ITCMRAM_BASE (0x00000000UL) /*!< Base address of : 64KB RAM reserved for CPU execution/instruction accessible over ITCM */ +#define D1_ITCMICP_BASE (0x00100000UL) /*!< Base address of : (up to 128KB) embedded Test FLASH memory accessible over ITCM */ +#define D1_DTCMRAM_BASE (0x20000000UL) /*!< Base address of : 128KB system data RAM accessible over DTCM */ +#define D1_AXIFLASH_BASE (0x08000000UL) /*!< Base address of : (up to 1 MB) embedded FLASH memory accessible over AXI */ +#define D1_AXIICP_BASE (0x1FF00000UL) /*!< Base address of : (up to 128KB) embedded Test FLASH memory accessible over AXI */ +#define D1_AXISRAM1_BASE (0x24000000UL) /*!< Base address of : (up to 128KB) system data RAM1 accessible over over AXI */ +#define D1_AXISRAM2_BASE (0x24020000UL) /*!< Base address of : (up to 192KB) system data RAM2 accessible over over AXI to be shared with ITCM (64K granularity) */ +#define D1_AXISRAM_BASE D1_AXISRAM1_BASE /*!< Base address of : (up to 320KB) system data RAM1/2 accessible over over AXI */ + +#define D2_AHBSRAM1_BASE (0x30000000UL) /*!< Base address of : (up to 16KB) system data RAM accessible over over AXI->AHB Bridge */ +#define D2_AHBSRAM2_BASE (0x30004000UL) /*!< Base address of : (up to 16KB) system data RAM accessible over over AXI->AHB Bridge */ +#define D2_AHBSRAM_BASE D2_AHBSRAM1_BASE /*!< Base address of : (up to 32KB) system data RAM1/2 accessible over over AXI->AHB Bridge */ + +#define D3_BKPSRAM_BASE (0x38800000UL) /*!< Base address of : Backup SRAM(4 KB) over AXI->AHB Bridge */ +#define D3_SRAM_BASE (0x38000000UL) /*!< Base address of : Backup SRAM(16 KB) over AXI->AHB Bridge */ + +#define PERIPH_BASE (0x40000000UL) /*!< Base address of : AHB/APB Peripherals */ +#define OCTOSPI1_BASE (0x90000000UL) /*!< Base address of : OCTOSPI1 memories accessible over AXI */ +#define OCTOSPI2_BASE (0x70000000UL) /*!< Base address of : OCTOSPI2 memories accessible over AXI */ + +#define FLASH_BANK1_BASE (0x08000000UL) /*!< Base address of : (up to 1 MB) Flash Bank1 accessible over AXI */ +#define FLASH_END (0x080FFFFFUL) /*!< FLASH end address */ + + +/* Legacy define */ +#define FLASH_BASE FLASH_BANK1_BASE + +/*!< Device electronic signature memory map */ +#define UID_BASE (0x1FF1E800UL) /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE (0x1FF1E880UL) /*!< FLASH Size register base address */ + + +/*!< Peripheral memory map */ +#define D2_APB1PERIPH_BASE PERIPH_BASE +#define D2_APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define D2_AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define D2_AHB2PERIPH_BASE (PERIPH_BASE + 0x08020000UL) + +#define D1_APB1PERIPH_BASE (PERIPH_BASE + 0x10000000UL) +#define D1_AHB1PERIPH_BASE (PERIPH_BASE + 0x12000000UL) + +#define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL) +#define D3_AHB1PERIPH_BASE (PERIPH_BASE + 0x18020000UL) + +/*!< Legacy Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL) + + +/*!< D1_AHB1PERIPH peripherals */ + +#define MDMA_BASE (D1_AHB1PERIPH_BASE + 0x0000UL) +#define DMA2D_BASE (D1_AHB1PERIPH_BASE + 0x1000UL) +#define FLASH_R_BASE (D1_AHB1PERIPH_BASE + 0x2000UL) +#define FMC_R_BASE (D1_AHB1PERIPH_BASE + 0x4000UL) +#define OCTOSPI1_R_BASE (D1_AHB1PERIPH_BASE + 0x5000UL) +#define DLYB_OCTOSPI1_BASE (D1_AHB1PERIPH_BASE + 0x6000UL) +#define SDMMC1_BASE (D1_AHB1PERIPH_BASE + 0x7000UL) +#define DLYB_SDMMC1_BASE (D1_AHB1PERIPH_BASE + 0x8000UL) +#define RAMECC1_BASE (D1_AHB1PERIPH_BASE + 0x9000UL) +#define OCTOSPI2_R_BASE (D1_AHB1PERIPH_BASE + 0xA000UL) +#define DLYB_OCTOSPI2_BASE (D1_AHB1PERIPH_BASE + 0xB000UL) +#define OCTOSPIM_BASE (D1_AHB1PERIPH_BASE + 0xB400UL) + +#define OTFDEC1_BASE (D1_AHB1PERIPH_BASE + 0xB800UL) +#define OTFDEC1_REGION1_BASE (OTFDEC1_BASE + 0x20UL) +#define OTFDEC1_REGION2_BASE (OTFDEC1_BASE + 0x50UL) +#define OTFDEC1_REGION3_BASE (OTFDEC1_BASE + 0x80UL) +#define OTFDEC1_REGION4_BASE (OTFDEC1_BASE + 0xB0UL) +#define OTFDEC2_BASE (D1_AHB1PERIPH_BASE + 0xBC00UL) +#define OTFDEC2_REGION1_BASE (OTFDEC2_BASE + 0x20UL) +#define OTFDEC2_REGION2_BASE (OTFDEC2_BASE + 0x50UL) +#define OTFDEC2_REGION3_BASE (OTFDEC2_BASE + 0x80UL) +#define OTFDEC2_REGION4_BASE (OTFDEC2_BASE + 0xB0UL) + +/*!< D2_AHB1PERIPH peripherals */ + +#define DMA1_BASE (D2_AHB1PERIPH_BASE + 0x0000UL) +#define DMA2_BASE (D2_AHB1PERIPH_BASE + 0x0400UL) +#define DMAMUX1_BASE (D2_AHB1PERIPH_BASE + 0x0800UL) +#define ADC1_BASE (D2_AHB1PERIPH_BASE + 0x2000UL) +#define ADC2_BASE (D2_AHB1PERIPH_BASE + 0x2100UL) +#define ADC12_COMMON_BASE (D2_AHB1PERIPH_BASE + 0x2300UL) +#define ETH_BASE (D2_AHB1PERIPH_BASE + 0x8000UL) +#define ETH_MAC_BASE (ETH_BASE) + +/*!< USB registers base address */ +#define USB1_OTG_HS_PERIPH_BASE (0x40040000UL) +#define USB_OTG_GLOBAL_BASE (0x000UL) +#define USB_OTG_DEVICE_BASE (0x800UL) +#define USB_OTG_IN_ENDPOINT_BASE (0x900UL) +#define USB_OTG_OUT_ENDPOINT_BASE (0xB00UL) +#define USB_OTG_EP_REG_SIZE (0x20UL) +#define USB_OTG_HOST_BASE (0x400UL) +#define USB_OTG_HOST_PORT_BASE (0x440UL) +#define USB_OTG_HOST_CHANNEL_BASE (0x500UL) +#define USB_OTG_HOST_CHANNEL_SIZE (0x20UL) +#define USB_OTG_PCGCCTL_BASE (0xE00UL) +#define USB_OTG_FIFO_BASE (0x1000UL) +#define USB_OTG_FIFO_SIZE (0x1000UL) + +/*!< D2_AHB2PERIPH peripherals */ + +#define DCMI_BASE (D2_AHB2PERIPH_BASE + 0x0000UL) +#define PSSI_BASE (D2_AHB2PERIPH_BASE + 0x0400UL) +#define CRYP_BASE (D2_AHB2PERIPH_BASE + 0x1000UL) +#define HASH_BASE (D2_AHB2PERIPH_BASE + 0x1400UL) +#define HASH_DIGEST_BASE (D2_AHB2PERIPH_BASE + 0x1710UL) +#define RNG_BASE (D2_AHB2PERIPH_BASE + 0x1800UL) +#define SDMMC2_BASE (D2_AHB2PERIPH_BASE + 0x2400UL) +#define DLYB_SDMMC2_BASE (D2_AHB2PERIPH_BASE + 0x2800UL) +#define RAMECC2_BASE (D2_AHB2PERIPH_BASE + 0x3000UL) +#define FMAC_BASE (D2_AHB2PERIPH_BASE + 0x4000UL) +#define CORDIC_BASE (D2_AHB2PERIPH_BASE + 0x4400UL) + +/*!< D3_AHB1PERIPH peripherals */ +#define GPIOA_BASE (D3_AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (D3_AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (D3_AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (D3_AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (D3_AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (D3_AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (D3_AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (D3_AHB1PERIPH_BASE + 0x1C00UL) +#define GPIOJ_BASE (D3_AHB1PERIPH_BASE + 0x2400UL) +#define GPIOK_BASE (D3_AHB1PERIPH_BASE + 0x2800UL) +#define RCC_BASE (D3_AHB1PERIPH_BASE + 0x4400UL) +#define PWR_BASE (D3_AHB1PERIPH_BASE + 0x4800UL) +#define CRC_BASE (D3_AHB1PERIPH_BASE + 0x4C00UL) +#define BDMA_BASE (D3_AHB1PERIPH_BASE + 0x5400UL) +#define DMAMUX2_BASE (D3_AHB1PERIPH_BASE + 0x5800UL) +#define ADC3_BASE (D3_AHB1PERIPH_BASE + 0x6000UL) +#define ADC3_COMMON_BASE (D3_AHB1PERIPH_BASE + 0x6300UL) +#define HSEM_BASE (D3_AHB1PERIPH_BASE + 0x6400UL) +#define RAMECC3_BASE (D3_AHB1PERIPH_BASE + 0x7000UL) + +/*!< D1_APB1PERIPH peripherals */ +#define LTDC_BASE (D1_APB1PERIPH_BASE + 0x1000UL) +#define LTDC_Layer1_BASE (LTDC_BASE + 0x84UL) +#define LTDC_Layer2_BASE (LTDC_BASE + 0x104UL) +#define WWDG1_BASE (D1_APB1PERIPH_BASE + 0x3000UL) + +/*!< D2_APB1PERIPH peripherals */ +#define TIM2_BASE (D2_APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (D2_APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (D2_APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (D2_APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (D2_APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (D2_APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (D2_APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (D2_APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (D2_APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (D2_APB1PERIPH_BASE + 0x2400UL) + + +#define SPI2_BASE (D2_APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (D2_APB1PERIPH_BASE + 0x3C00UL) +#define SPDIFRX_BASE (D2_APB1PERIPH_BASE + 0x4000UL) +#define USART2_BASE (D2_APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (D2_APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (D2_APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (D2_APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (D2_APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (D2_APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (D2_APB1PERIPH_BASE + 0x5C00UL) +#define I2C5_BASE (D2_APB1PERIPH_BASE + 0x6400UL) +#define CEC_BASE (D2_APB1PERIPH_BASE + 0x6C00UL) +#define DAC1_BASE (D2_APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (D2_APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (D2_APB1PERIPH_BASE + 0x7C00UL) +#define CRS_BASE (D2_APB1PERIPH_BASE + 0x8400UL) +#define SWPMI1_BASE (D2_APB1PERIPH_BASE + 0x8800UL) +#define OPAMP_BASE (D2_APB1PERIPH_BASE + 0x9000UL) +#define OPAMP1_BASE (D2_APB1PERIPH_BASE + 0x9000UL) +#define OPAMP2_BASE (D2_APB1PERIPH_BASE + 0x9010UL) +#define MDIOS_BASE (D2_APB1PERIPH_BASE + 0x9400UL) +#define FDCAN1_BASE (D2_APB1PERIPH_BASE + 0xA000UL) +#define FDCAN2_BASE (D2_APB1PERIPH_BASE + 0xA400UL) +#define FDCAN_CCU_BASE (D2_APB1PERIPH_BASE + 0xA800UL) +#define SRAMCAN_BASE (D2_APB1PERIPH_BASE + 0xAC00UL) +#define FDCAN3_BASE (D2_APB1PERIPH_BASE + 0xD400UL) +#define TIM23_BASE (D2_APB1PERIPH_BASE + 0xE000UL) +#define TIM24_BASE (D2_APB1PERIPH_BASE + 0xE400UL) + +/*!< D2_APB2PERIPH peripherals */ + +#define TIM1_BASE (D2_APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (D2_APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (D2_APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (D2_APB2PERIPH_BASE + 0x1400UL) +#define UART9_BASE (D2_APB2PERIPH_BASE + 0x1800UL) +#define USART10_BASE (D2_APB2PERIPH_BASE + 0x1C00UL) +#define SPI1_BASE (D2_APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (D2_APB2PERIPH_BASE + 0x3400UL) +#define TIM15_BASE (D2_APB2PERIPH_BASE + 0x4000UL) +#define TIM16_BASE (D2_APB2PERIPH_BASE + 0x4400UL) +#define TIM17_BASE (D2_APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (D2_APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (D2_APB2PERIPH_BASE + 0x5800UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) +#define DFSDM1_BASE (D2_APB2PERIPH_BASE + 0x7800UL) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00UL) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20UL) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40UL) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60UL) +#define DFSDM1_Channel4_BASE (DFSDM1_BASE + 0x80UL) +#define DFSDM1_Channel5_BASE (DFSDM1_BASE + 0xA0UL) +#define DFSDM1_Channel6_BASE (DFSDM1_BASE + 0xC0UL) +#define DFSDM1_Channel7_BASE (DFSDM1_BASE + 0xE0UL) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100UL) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180UL) +#define DFSDM1_Filter2_BASE (DFSDM1_BASE + 0x200UL) +#define DFSDM1_Filter3_BASE (DFSDM1_BASE + 0x280UL) + + +/*!< D3_APB1PERIPH peripherals */ +#define EXTI_BASE (D3_APB1PERIPH_BASE + 0x0000UL) +#define EXTI_D1_BASE (EXTI_BASE + 0x0080UL) +#define EXTI_D2_BASE (EXTI_BASE + 0x00C0UL) +#define SYSCFG_BASE (D3_APB1PERIPH_BASE + 0x0400UL) +#define LPUART1_BASE (D3_APB1PERIPH_BASE + 0x0C00UL) +#define SPI6_BASE (D3_APB1PERIPH_BASE + 0x1400UL) +#define I2C4_BASE (D3_APB1PERIPH_BASE + 0x1C00UL) +#define LPTIM2_BASE (D3_APB1PERIPH_BASE + 0x2400UL) +#define LPTIM3_BASE (D3_APB1PERIPH_BASE + 0x2800UL) +#define LPTIM4_BASE (D3_APB1PERIPH_BASE + 0x2C00UL) +#define LPTIM5_BASE (D3_APB1PERIPH_BASE + 0x3000UL) +#define COMP12_BASE (D3_APB1PERIPH_BASE + 0x3800UL) +#define COMP1_BASE (COMP12_BASE + 0x0CUL) +#define COMP2_BASE (COMP12_BASE + 0x10UL) +#define VREFBUF_BASE (D3_APB1PERIPH_BASE + 0x3C00UL) +#define RTC_BASE (D3_APB1PERIPH_BASE + 0x4000UL) +#define IWDG1_BASE (D3_APB1PERIPH_BASE + 0x4800UL) + + +#define SAI4_BASE (D3_APB1PERIPH_BASE + 0x5400UL) +#define SAI4_Block_A_BASE (SAI4_BASE + 0x004UL) +#define SAI4_Block_B_BASE (SAI4_BASE + 0x024UL) + +#define DTS_BASE (D3_APB1PERIPH_BASE + 0x6800UL) + + + +#define BDMA_Channel0_BASE (BDMA_BASE + 0x0008UL) +#define BDMA_Channel1_BASE (BDMA_BASE + 0x001CUL) +#define BDMA_Channel2_BASE (BDMA_BASE + 0x0030UL) +#define BDMA_Channel3_BASE (BDMA_BASE + 0x0044UL) +#define BDMA_Channel4_BASE (BDMA_BASE + 0x0058UL) +#define BDMA_Channel5_BASE (BDMA_BASE + 0x006CUL) +#define BDMA_Channel6_BASE (BDMA_BASE + 0x0080UL) +#define BDMA_Channel7_BASE (BDMA_BASE + 0x0094UL) + +#define DMAMUX2_Channel0_BASE (DMAMUX2_BASE) +#define DMAMUX2_Channel1_BASE (DMAMUX2_BASE + 0x0004UL) +#define DMAMUX2_Channel2_BASE (DMAMUX2_BASE + 0x0008UL) +#define DMAMUX2_Channel3_BASE (DMAMUX2_BASE + 0x000CUL) +#define DMAMUX2_Channel4_BASE (DMAMUX2_BASE + 0x0010UL) +#define DMAMUX2_Channel5_BASE (DMAMUX2_BASE + 0x0014UL) +#define DMAMUX2_Channel6_BASE (DMAMUX2_BASE + 0x0018UL) +#define DMAMUX2_Channel7_BASE (DMAMUX2_BASE + 0x001CUL) + +#define DMAMUX2_RequestGenerator0_BASE (DMAMUX2_BASE + 0x0100UL) +#define DMAMUX2_RequestGenerator1_BASE (DMAMUX2_BASE + 0x0104UL) +#define DMAMUX2_RequestGenerator2_BASE (DMAMUX2_BASE + 0x0108UL) +#define DMAMUX2_RequestGenerator3_BASE (DMAMUX2_BASE + 0x010CUL) +#define DMAMUX2_RequestGenerator4_BASE (DMAMUX2_BASE + 0x0110UL) +#define DMAMUX2_RequestGenerator5_BASE (DMAMUX2_BASE + 0x0114UL) +#define DMAMUX2_RequestGenerator6_BASE (DMAMUX2_BASE + 0x0118UL) +#define DMAMUX2_RequestGenerator7_BASE (DMAMUX2_BASE + 0x011CUL) + +#define DMAMUX2_ChannelStatus_BASE (DMAMUX2_BASE + 0x0080UL) +#define DMAMUX2_RequestGenStatus_BASE (DMAMUX2_BASE + 0x0140UL) + +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) + +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) + +#define DMAMUX1_Channel0_BASE (DMAMUX1_BASE) +#define DMAMUX1_Channel1_BASE (DMAMUX1_BASE + 0x0004UL) +#define DMAMUX1_Channel2_BASE (DMAMUX1_BASE + 0x0008UL) +#define DMAMUX1_Channel3_BASE (DMAMUX1_BASE + 0x000CUL) +#define DMAMUX1_Channel4_BASE (DMAMUX1_BASE + 0x0010UL) +#define DMAMUX1_Channel5_BASE (DMAMUX1_BASE + 0x0014UL) +#define DMAMUX1_Channel6_BASE (DMAMUX1_BASE + 0x0018UL) +#define DMAMUX1_Channel7_BASE (DMAMUX1_BASE + 0x001CUL) +#define DMAMUX1_Channel8_BASE (DMAMUX1_BASE + 0x0020UL) +#define DMAMUX1_Channel9_BASE (DMAMUX1_BASE + 0x0024UL) +#define DMAMUX1_Channel10_BASE (DMAMUX1_BASE + 0x0028UL) +#define DMAMUX1_Channel11_BASE (DMAMUX1_BASE + 0x002CUL) +#define DMAMUX1_Channel12_BASE (DMAMUX1_BASE + 0x0030UL) +#define DMAMUX1_Channel13_BASE (DMAMUX1_BASE + 0x0034UL) +#define DMAMUX1_Channel14_BASE (DMAMUX1_BASE + 0x0038UL) +#define DMAMUX1_Channel15_BASE (DMAMUX1_BASE + 0x003CUL) + +#define DMAMUX1_RequestGenerator0_BASE (DMAMUX1_BASE + 0x0100UL) +#define DMAMUX1_RequestGenerator1_BASE (DMAMUX1_BASE + 0x0104UL) +#define DMAMUX1_RequestGenerator2_BASE (DMAMUX1_BASE + 0x0108UL) +#define DMAMUX1_RequestGenerator3_BASE (DMAMUX1_BASE + 0x010CUL) +#define DMAMUX1_RequestGenerator4_BASE (DMAMUX1_BASE + 0x0110UL) +#define DMAMUX1_RequestGenerator5_BASE (DMAMUX1_BASE + 0x0114UL) +#define DMAMUX1_RequestGenerator6_BASE (DMAMUX1_BASE + 0x0118UL) +#define DMAMUX1_RequestGenerator7_BASE (DMAMUX1_BASE + 0x011CUL) + +#define DMAMUX1_ChannelStatus_BASE (DMAMUX1_BASE + 0x0080UL) +#define DMAMUX1_RequestGenStatus_BASE (DMAMUX1_BASE + 0x0140UL) + +/*!< FMC Banks registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank2_R_BASE (FMC_R_BASE + 0x0060UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define FMC_Bank5_6_R_BASE (FMC_R_BASE + 0x0140UL) + +/* Debug MCU registers base address */ +#define DBGMCU_BASE (0x5C001000UL) + +#define MDMA_Channel0_BASE (MDMA_BASE + 0x00000040UL) +#define MDMA_Channel1_BASE (MDMA_BASE + 0x00000080UL) +#define MDMA_Channel2_BASE (MDMA_BASE + 0x000000C0UL) +#define MDMA_Channel3_BASE (MDMA_BASE + 0x00000100UL) +#define MDMA_Channel4_BASE (MDMA_BASE + 0x00000140UL) +#define MDMA_Channel5_BASE (MDMA_BASE + 0x00000180UL) +#define MDMA_Channel6_BASE (MDMA_BASE + 0x000001C0UL) +#define MDMA_Channel7_BASE (MDMA_BASE + 0x00000200UL) +#define MDMA_Channel8_BASE (MDMA_BASE + 0x00000240UL) +#define MDMA_Channel9_BASE (MDMA_BASE + 0x00000280UL) +#define MDMA_Channel10_BASE (MDMA_BASE + 0x000002C0UL) +#define MDMA_Channel11_BASE (MDMA_BASE + 0x00000300UL) +#define MDMA_Channel12_BASE (MDMA_BASE + 0x00000340UL) +#define MDMA_Channel13_BASE (MDMA_BASE + 0x00000380UL) +#define MDMA_Channel14_BASE (MDMA_BASE + 0x000003C0UL) +#define MDMA_Channel15_BASE (MDMA_BASE + 0x00000400UL) + +#define RAMECC1_Monitor1_BASE (RAMECC1_BASE + 0x20UL) +#define RAMECC1_Monitor2_BASE (RAMECC1_BASE + 0x40UL) +#define RAMECC1_Monitor3_BASE (RAMECC1_BASE + 0x60UL) +#define RAMECC1_Monitor4_BASE (RAMECC1_BASE + 0x80UL) +#define RAMECC1_Monitor5_BASE (RAMECC1_BASE + 0xA0UL) +#define RAMECC1_Monitor6_BASE (RAMECC1_BASE + 0xC0UL) + +#define RAMECC2_Monitor1_BASE (RAMECC2_BASE + 0x20UL) +#define RAMECC2_Monitor2_BASE (RAMECC2_BASE + 0x40UL) +#define RAMECC2_Monitor3_BASE (RAMECC2_BASE + 0x60UL) + +#define RAMECC3_Monitor1_BASE (RAMECC3_BASE + 0x20UL) +#define RAMECC3_Monitor2_BASE (RAMECC3_BASE + 0x40UL) + + + +#define GPV_BASE (PERIPH_BASE + 0x11000000UL) /*!< GPV_BASE (PERIPH_BASE + 0x11000000UL) */ + +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define VREFBUF ((VREFBUF_TypeDef *) VREFBUF_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG1 ((WWDG_TypeDef *) WWDG1_BASE) + + +#define IWDG1 ((IWDG_TypeDef *) IWDG1_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define SPI6 ((SPI_TypeDef *) SPI6_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define USART10 ((USART_TypeDef *) USART10_BASE) +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define CRS ((CRS_TypeDef *) CRS_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define I2C4 ((I2C_TypeDef *) I2C4_BASE) +#define I2C5 ((I2C_TypeDef *) I2C5_BASE) +#define FDCAN1 ((FDCAN_GlobalTypeDef *) FDCAN1_BASE) +#define FDCAN2 ((FDCAN_GlobalTypeDef *) FDCAN2_BASE) +#define FDCAN_CCU ((FDCAN_ClockCalibrationUnit_TypeDef *) FDCAN_CCU_BASE) +#define FDCAN3 ((FDCAN_GlobalTypeDef *) FDCAN3_BASE) +#define TIM23 ((TIM_TypeDef *) TIM23_BASE) +#define TIM24 ((TIM_TypeDef *) TIM24_BASE) +#define CEC ((CEC_TypeDef *) CEC_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC1_BASE) +#define LPUART1 ((USART_TypeDef *) LPUART1_BASE) +#define SWPMI1 ((SWPMI_TypeDef *) SWPMI1_BASE) +#define LPTIM2 ((LPTIM_TypeDef *) LPTIM2_BASE) +#define LPTIM3 ((LPTIM_TypeDef *) LPTIM3_BASE) +#define DTS ((DTS_TypeDef *) DTS_BASE) +#define LPTIM4 ((LPTIM_TypeDef *) LPTIM4_BASE) +#define LPTIM5 ((LPTIM_TypeDef *) LPTIM5_BASE) + +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define COMP12 ((COMPOPT_TypeDef *) COMP12_BASE) +#define COMP1 ((COMP_TypeDef *) COMP1_BASE) +#define COMP2 ((COMP_TypeDef *) COMP2_BASE) +#define COMP12_COMMON ((COMP_Common_TypeDef *) COMP2_BASE) +#define OPAMP ((OPAMP_TypeDef *) OPAMP_BASE) +#define OPAMP1 ((OPAMP_TypeDef *) OPAMP1_BASE) +#define OPAMP2 ((OPAMP_TypeDef *) OPAMP2_BASE) + + +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define EXTI_D1 ((EXTI_Core_TypeDef *) EXTI_D1_BASE) +#define EXTI_D2 ((EXTI_Core_TypeDef *) EXTI_D2_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM15 ((TIM_TypeDef *) TIM15_BASE) +#define TIM16 ((TIM_TypeDef *) TIM16_BASE) +#define TIM17 ((TIM_TypeDef *) TIM17_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define SAI4 ((SAI_TypeDef *) SAI4_BASE) +#define SAI4_Block_A ((SAI_Block_TypeDef *)SAI4_Block_A_BASE) +#define SAI4_Block_B ((SAI_Block_TypeDef *)SAI4_Block_B_BASE) + +#define SPDIFRX ((SPDIFRX_TypeDef *) SPDIFRX_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel4_BASE) +#define DFSDM1_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel5_BASE) +#define DFSDM1_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel6_BASE) +#define DFSDM1_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel7_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM1_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter2_BASE) +#define DFSDM1_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter3_BASE) +#define DMA2D ((DMA2D_TypeDef *) DMA2D_BASE) +#define DCMI ((DCMI_TypeDef *) DCMI_BASE) +#define PSSI ((PSSI_TypeDef *) PSSI_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) + +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) +#define GPIOK ((GPIO_TypeDef *) GPIOK_BASE) + +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC3_COMMON ((ADC_Common_TypeDef *) ADC3_COMMON_BASE) +#define ADC12_COMMON ((ADC_Common_TypeDef *) ADC12_COMMON_BASE) + +#define CRYP ((CRYP_TypeDef *) CRYP_BASE) +#define HASH ((HASH_TypeDef *) HASH_BASE) +#define HASH_DIGEST ((HASH_DIGEST_TypeDef *) HASH_DIGEST_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define SDMMC2 ((SDMMC_TypeDef *) SDMMC2_BASE) +#define DLYB_SDMMC2 ((DLYB_TypeDef *) DLYB_SDMMC2_BASE) +#define FMAC ((FMAC_TypeDef *) FMAC_BASE) +#define CORDIC ((CORDIC_TypeDef *) CORDIC_BASE) + +#define BDMA ((BDMA_TypeDef *) BDMA_BASE) +#define BDMA_Channel0 ((BDMA_Channel_TypeDef *) BDMA_Channel0_BASE) +#define BDMA_Channel1 ((BDMA_Channel_TypeDef *) BDMA_Channel1_BASE) +#define BDMA_Channel2 ((BDMA_Channel_TypeDef *) BDMA_Channel2_BASE) +#define BDMA_Channel3 ((BDMA_Channel_TypeDef *) BDMA_Channel3_BASE) +#define BDMA_Channel4 ((BDMA_Channel_TypeDef *) BDMA_Channel4_BASE) +#define BDMA_Channel5 ((BDMA_Channel_TypeDef *) BDMA_Channel5_BASE) +#define BDMA_Channel6 ((BDMA_Channel_TypeDef *) BDMA_Channel6_BASE) +#define BDMA_Channel7 ((BDMA_Channel_TypeDef *) BDMA_Channel7_BASE) + +#define RAMECC1 ((RAMECC_TypeDef *)RAMECC1_BASE) +#define RAMECC1_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor1_BASE) +#define RAMECC1_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor2_BASE) +#define RAMECC1_Monitor3 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor3_BASE) +#define RAMECC1_Monitor4 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor4_BASE) +#define RAMECC1_Monitor5 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor5_BASE) +#define RAMECC1_Monitor6 ((RAMECC_MonitorTypeDef *)RAMECC1_Monitor6_BASE) + +#define RAMECC2 ((RAMECC_TypeDef *)RAMECC2_BASE) +#define RAMECC2_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor1_BASE) +#define RAMECC2_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor2_BASE) +#define RAMECC2_Monitor3 ((RAMECC_MonitorTypeDef *)RAMECC2_Monitor3_BASE) + +#define RAMECC3 ((RAMECC_TypeDef *)RAMECC3_BASE) +#define RAMECC3_Monitor1 ((RAMECC_MonitorTypeDef *)RAMECC3_Monitor1_BASE) +#define RAMECC3_Monitor2 ((RAMECC_MonitorTypeDef *)RAMECC3_Monitor2_BASE) + +#define DMAMUX2 ((DMAMUX_Channel_TypeDef *) DMAMUX2_BASE) +#define DMAMUX2_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel0_BASE) +#define DMAMUX2_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel1_BASE) +#define DMAMUX2_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel2_BASE) +#define DMAMUX2_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel3_BASE) +#define DMAMUX2_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel4_BASE) +#define DMAMUX2_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel5_BASE) +#define DMAMUX2_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel6_BASE) +#define DMAMUX2_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX2_Channel7_BASE) + + +#define DMAMUX2_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator0_BASE) +#define DMAMUX2_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator1_BASE) +#define DMAMUX2_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator2_BASE) +#define DMAMUX2_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator3_BASE) +#define DMAMUX2_RequestGenerator4 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator4_BASE) +#define DMAMUX2_RequestGenerator5 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator5_BASE) +#define DMAMUX2_RequestGenerator6 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator6_BASE) +#define DMAMUX2_RequestGenerator7 ((DMAMUX_RequestGen_TypeDef *) DMAMUX2_RequestGenerator7_BASE) + +#define DMAMUX2_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX2_ChannelStatus_BASE) +#define DMAMUX2_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX2_RequestGenStatus_BASE) + +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) + +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) + + +#define DMAMUX1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_BASE) +#define DMAMUX1_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel0_BASE) +#define DMAMUX1_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel1_BASE) +#define DMAMUX1_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel2_BASE) +#define DMAMUX1_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel3_BASE) +#define DMAMUX1_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel4_BASE) +#define DMAMUX1_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel5_BASE) +#define DMAMUX1_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel6_BASE) +#define DMAMUX1_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel7_BASE) +#define DMAMUX1_Channel8 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel8_BASE) +#define DMAMUX1_Channel9 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel9_BASE) +#define DMAMUX1_Channel10 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel10_BASE) +#define DMAMUX1_Channel11 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel11_BASE) +#define DMAMUX1_Channel12 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel12_BASE) +#define DMAMUX1_Channel13 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel13_BASE) +#define DMAMUX1_Channel14 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel14_BASE) +#define DMAMUX1_Channel15 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel15_BASE) + +#define DMAMUX1_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator0_BASE) +#define DMAMUX1_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator1_BASE) +#define DMAMUX1_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator2_BASE) +#define DMAMUX1_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator3_BASE) +#define DMAMUX1_RequestGenerator4 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator4_BASE) +#define DMAMUX1_RequestGenerator5 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator5_BASE) +#define DMAMUX1_RequestGenerator6 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator6_BASE) +#define DMAMUX1_RequestGenerator7 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator7_BASE) + +#define DMAMUX1_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX1_ChannelStatus_BASE) +#define DMAMUX1_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX1_RequestGenStatus_BASE) + + +#define FMC_Bank1_R ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E_R ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank2_R ((FMC_Bank2_TypeDef *) FMC_Bank2_R_BASE) +#define FMC_Bank3_R ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) +#define FMC_Bank5_6_R ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) + +#define OCTOSPI1 ((OCTOSPI_TypeDef *) OCTOSPI1_R_BASE) +#define DLYB_OCTOSPI1 ((DLYB_TypeDef *) DLYB_OCTOSPI1_BASE) +#define OCTOSPI2 ((OCTOSPI_TypeDef *) OCTOSPI2_R_BASE) +#define DLYB_OCTOSPI2 ((DLYB_TypeDef *) DLYB_OCTOSPI2_BASE) +#define OCTOSPIM ((OCTOSPIM_TypeDef *) OCTOSPIM_BASE) + +#define OTFDEC1 ((OTFDEC_TypeDef *) OTFDEC1_BASE) +#define OTFDEC1_REGION1 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION1_BASE) +#define OTFDEC1_REGION2 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION2_BASE) +#define OTFDEC1_REGION3 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION3_BASE) +#define OTFDEC1_REGION4 ((OTFDEC_Region_TypeDef *) OTFDEC1_REGION4_BASE) + +#define OTFDEC2 ((OTFDEC_TypeDef *) OTFDEC2_BASE) +#define OTFDEC2_REGION1 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION1_BASE) +#define OTFDEC2_REGION2 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION2_BASE) +#define OTFDEC2_REGION3 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION3_BASE) +#define OTFDEC2_REGION4 ((OTFDEC_Region_TypeDef *) OTFDEC2_REGION4_BASE) + +#define SDMMC1 ((SDMMC_TypeDef *) SDMMC1_BASE) +#define DLYB_SDMMC1 ((DLYB_TypeDef *) DLYB_SDMMC1_BASE) + +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +#define HSEM ((HSEM_TypeDef *) HSEM_BASE) +#define HSEM_COMMON ((HSEM_Common_TypeDef *) (HSEM_BASE + 0x100UL)) + +#define LTDC ((LTDC_TypeDef *)LTDC_BASE) +#define LTDC_Layer1 ((LTDC_Layer_TypeDef *)LTDC_Layer1_BASE) +#define LTDC_Layer2 ((LTDC_Layer_TypeDef *)LTDC_Layer2_BASE) + +#define MDIOS ((MDIOS_TypeDef *) MDIOS_BASE) + +#define ETH ((ETH_TypeDef *)ETH_BASE) +#define MDMA ((MDMA_TypeDef *)MDMA_BASE) +#define MDMA_Channel0 ((MDMA_Channel_TypeDef *)MDMA_Channel0_BASE) +#define MDMA_Channel1 ((MDMA_Channel_TypeDef *)MDMA_Channel1_BASE) +#define MDMA_Channel2 ((MDMA_Channel_TypeDef *)MDMA_Channel2_BASE) +#define MDMA_Channel3 ((MDMA_Channel_TypeDef *)MDMA_Channel3_BASE) +#define MDMA_Channel4 ((MDMA_Channel_TypeDef *)MDMA_Channel4_BASE) +#define MDMA_Channel5 ((MDMA_Channel_TypeDef *)MDMA_Channel5_BASE) +#define MDMA_Channel6 ((MDMA_Channel_TypeDef *)MDMA_Channel6_BASE) +#define MDMA_Channel7 ((MDMA_Channel_TypeDef *)MDMA_Channel7_BASE) +#define MDMA_Channel8 ((MDMA_Channel_TypeDef *)MDMA_Channel8_BASE) +#define MDMA_Channel9 ((MDMA_Channel_TypeDef *)MDMA_Channel9_BASE) +#define MDMA_Channel10 ((MDMA_Channel_TypeDef *)MDMA_Channel10_BASE) +#define MDMA_Channel11 ((MDMA_Channel_TypeDef *)MDMA_Channel11_BASE) +#define MDMA_Channel12 ((MDMA_Channel_TypeDef *)MDMA_Channel12_BASE) +#define MDMA_Channel13 ((MDMA_Channel_TypeDef *)MDMA_Channel13_BASE) +#define MDMA_Channel14 ((MDMA_Channel_TypeDef *)MDMA_Channel14_BASE) +#define MDMA_Channel15 ((MDMA_Channel_TypeDef *)MDMA_Channel15_BASE) + + +#define USB1_OTG_HS ((USB_OTG_GlobalTypeDef *) USB1_OTG_HS_PERIPH_BASE) + +/* Legacy defines */ +#define USB_OTG_HS USB1_OTG_HS +#define USB_OTG_HS_PERIPH_BASE USB1_OTG_HS_PERIPH_BASE + +#define GPV ((GPV_TypeDef *) GPV_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ +/******************************* ADC VERSION ********************************/ +#define ADC_VER_V5_V90 +/******************** Bit definition for ADC_ISR register ********************/ +#define ADC_ISR_ADRDY_Pos (0U) +#define ADC_ISR_ADRDY_Msk (0x1UL << ADC_ISR_ADRDY_Pos) /*!< 0x00000001 */ +#define ADC_ISR_ADRDY ADC_ISR_ADRDY_Msk /*!< ADC Ready (ADRDY) flag */ +#define ADC_ISR_EOSMP_Pos (1U) +#define ADC_ISR_EOSMP_Msk (0x1UL << ADC_ISR_EOSMP_Pos) /*!< 0x00000002 */ +#define ADC_ISR_EOSMP ADC_ISR_EOSMP_Msk /*!< ADC End of Sampling flag */ +#define ADC_ISR_EOC_Pos (2U) +#define ADC_ISR_EOC_Msk (0x1UL << ADC_ISR_EOC_Pos) /*!< 0x00000004 */ +#define ADC_ISR_EOC ADC_ISR_EOC_Msk /*!< ADC End of Regular Conversion flag */ +#define ADC_ISR_EOS_Pos (3U) +#define ADC_ISR_EOS_Msk (0x1UL << ADC_ISR_EOS_Pos) /*!< 0x00000008 */ +#define ADC_ISR_EOS ADC_ISR_EOS_Msk /*!< ADC End of Regular sequence of Conversions flag */ +#define ADC_ISR_OVR_Pos (4U) +#define ADC_ISR_OVR_Msk (0x1UL << ADC_ISR_OVR_Pos) /*!< 0x00000010 */ +#define ADC_ISR_OVR ADC_ISR_OVR_Msk /*!< ADC overrun flag */ +#define ADC_ISR_JEOC_Pos (5U) +#define ADC_ISR_JEOC_Msk (0x1UL << ADC_ISR_JEOC_Pos) /*!< 0x00000020 */ +#define ADC_ISR_JEOC ADC_ISR_JEOC_Msk /*!< ADC End of Injected Conversion flag */ +#define ADC_ISR_JEOS_Pos (6U) +#define ADC_ISR_JEOS_Msk (0x1UL << ADC_ISR_JEOS_Pos) /*!< 0x00000040 */ +#define ADC_ISR_JEOS ADC_ISR_JEOS_Msk /*!< ADC End of Injected sequence of Conversions flag */ +#define ADC_ISR_AWD1_Pos (7U) +#define ADC_ISR_AWD1_Msk (0x1UL << ADC_ISR_AWD1_Pos) /*!< 0x00000080 */ +#define ADC_ISR_AWD1 ADC_ISR_AWD1_Msk /*!< ADC Analog watchdog 1 flag */ +#define ADC_ISR_AWD2_Pos (8U) +#define ADC_ISR_AWD2_Msk (0x1UL << ADC_ISR_AWD2_Pos) /*!< 0x00000100 */ +#define ADC_ISR_AWD2 ADC_ISR_AWD2_Msk /*!< ADC Analog watchdog 2 flag */ +#define ADC_ISR_AWD3_Pos (9U) +#define ADC_ISR_AWD3_Msk (0x1UL << ADC_ISR_AWD3_Pos) /*!< 0x00000200 */ +#define ADC_ISR_AWD3 ADC_ISR_AWD3_Msk /*!< ADC Analog watchdog 3 flag */ +#define ADC_ISR_JQOVF_Pos (10U) +#define ADC_ISR_JQOVF_Msk (0x1UL << ADC_ISR_JQOVF_Pos) /*!< 0x00000400 */ +#define ADC_ISR_JQOVF ADC_ISR_JQOVF_Msk /*!< ADC Injected Context Queue Overflow flag */ +#define ADC_ISR_LDORDY_Pos (12U) +#define ADC_ISR_LDORDY_Msk (0x1UL << ADC_ISR_LDORDY_Pos) /*!< 0x00001000 */ +#define ADC_ISR_LDORDY ADC_ISR_LDORDY_Msk /*!< ADC LDO Ready (LDORDY) flag */ + +/******************** Bit definition for ADC_IER register ********************/ +#define ADC_IER_ADRDYIE_Pos (0U) +#define ADC_IER_ADRDYIE_Msk (0x1UL << ADC_IER_ADRDYIE_Pos) /*!< 0x00000001 */ +#define ADC_IER_ADRDYIE ADC_IER_ADRDYIE_Msk /*!< ADC Ready (ADRDY) interrupt source */ +#define ADC_IER_EOSMPIE_Pos (1U) +#define ADC_IER_EOSMPIE_Msk (0x1UL << ADC_IER_EOSMPIE_Pos) /*!< 0x00000002 */ +#define ADC_IER_EOSMPIE ADC_IER_EOSMPIE_Msk /*!< ADC End of Sampling interrupt source */ +#define ADC_IER_EOCIE_Pos (2U) +#define ADC_IER_EOCIE_Msk (0x1UL << ADC_IER_EOCIE_Pos) /*!< 0x00000004 */ +#define ADC_IER_EOCIE ADC_IER_EOCIE_Msk /*!< ADC End of Regular Conversion interrupt source */ +#define ADC_IER_EOSIE_Pos (3U) +#define ADC_IER_EOSIE_Msk (0x1UL << ADC_IER_EOSIE_Pos) /*!< 0x00000008 */ +#define ADC_IER_EOSIE ADC_IER_EOSIE_Msk /*!< ADC End of Regular sequence of Conversions interrupt source */ +#define ADC_IER_OVRIE_Pos (4U) +#define ADC_IER_OVRIE_Msk (0x1UL << ADC_IER_OVRIE_Pos) /*!< 0x00000010 */ +#define ADC_IER_OVRIE ADC_IER_OVRIE_Msk /*!< ADC overrun interrupt source */ +#define ADC_IER_JEOCIE_Pos (5U) +#define ADC_IER_JEOCIE_Msk (0x1UL << ADC_IER_JEOCIE_Pos) /*!< 0x00000020 */ +#define ADC_IER_JEOCIE ADC_IER_JEOCIE_Msk /*!< ADC End of Injected Conversion interrupt source */ +#define ADC_IER_JEOSIE_Pos (6U) +#define ADC_IER_JEOSIE_Msk (0x1UL << ADC_IER_JEOSIE_Pos) /*!< 0x00000040 */ +#define ADC_IER_JEOSIE ADC_IER_JEOSIE_Msk /*!< ADC End of Injected sequence of Conversions interrupt source */ +#define ADC_IER_AWD1IE_Pos (7U) +#define ADC_IER_AWD1IE_Msk (0x1UL << ADC_IER_AWD1IE_Pos) /*!< 0x00000080 */ +#define ADC_IER_AWD1IE ADC_IER_AWD1IE_Msk /*!< ADC Analog watchdog 1 interrupt source */ +#define ADC_IER_AWD2IE_Pos (8U) +#define ADC_IER_AWD2IE_Msk (0x1UL << ADC_IER_AWD2IE_Pos) /*!< 0x00000100 */ +#define ADC_IER_AWD2IE ADC_IER_AWD2IE_Msk /*!< ADC Analog watchdog 2 interrupt source */ +#define ADC_IER_AWD3IE_Pos (9U) +#define ADC_IER_AWD3IE_Msk (0x1UL << ADC_IER_AWD3IE_Pos) /*!< 0x00000200 */ +#define ADC_IER_AWD3IE ADC_IER_AWD3IE_Msk /*!< ADC Analog watchdog 3 interrupt source */ +#define ADC_IER_JQOVFIE_Pos (10U) +#define ADC_IER_JQOVFIE_Msk (0x1UL << ADC_IER_JQOVFIE_Pos) /*!< 0x00000400 */ +#define ADC_IER_JQOVFIE ADC_IER_JQOVFIE_Msk /*!< ADC Injected Context Queue Overflow interrupt source */ + +/******************** Bit definition for ADC_CR register ********************/ +#define ADC_CR_ADEN_Pos (0U) +#define ADC_CR_ADEN_Msk (0x1UL << ADC_CR_ADEN_Pos) /*!< 0x00000001 */ +#define ADC_CR_ADEN ADC_CR_ADEN_Msk /*!< ADC Enable control */ +#define ADC_CR_ADDIS_Pos (1U) +#define ADC_CR_ADDIS_Msk (0x1UL << ADC_CR_ADDIS_Pos) /*!< 0x00000002 */ +#define ADC_CR_ADDIS ADC_CR_ADDIS_Msk /*!< ADC Disable command */ +#define ADC_CR_ADSTART_Pos (2U) +#define ADC_CR_ADSTART_Msk (0x1UL << ADC_CR_ADSTART_Pos) /*!< 0x00000004 */ +#define ADC_CR_ADSTART ADC_CR_ADSTART_Msk /*!< ADC Start of Regular conversion */ +#define ADC_CR_JADSTART_Pos (3U) +#define ADC_CR_JADSTART_Msk (0x1UL << ADC_CR_JADSTART_Pos) /*!< 0x00000008 */ +#define ADC_CR_JADSTART ADC_CR_JADSTART_Msk /*!< ADC Start of injected conversion */ +#define ADC_CR_ADSTP_Pos (4U) +#define ADC_CR_ADSTP_Msk (0x1UL << ADC_CR_ADSTP_Pos) /*!< 0x00000010 */ +#define ADC_CR_ADSTP ADC_CR_ADSTP_Msk /*!< ADC Stop of Regular conversion */ +#define ADC_CR_JADSTP_Pos (5U) +#define ADC_CR_JADSTP_Msk (0x1UL << ADC_CR_JADSTP_Pos) /*!< 0x00000020 */ +#define ADC_CR_JADSTP ADC_CR_JADSTP_Msk /*!< ADC Stop of injected conversion */ +#define ADC_CR_BOOST_Pos (8U) +#define ADC_CR_BOOST_Msk (0x3UL << ADC_CR_BOOST_Pos) /*!< 0x00000300 */ +#define ADC_CR_BOOST ADC_CR_BOOST_Msk /*!< ADC Boost Mode configuration */ +#define ADC_CR_BOOST_0 (0x1UL << ADC_CR_BOOST_Pos) /*!< 0x00000100 */ +#define ADC_CR_BOOST_1 (0x2UL << ADC_CR_BOOST_Pos) /*!< 0x00000200 */ +#define ADC_CR_ADCALLIN_Pos (16U) +#define ADC_CR_ADCALLIN_Msk (0x1UL << ADC_CR_ADCALLIN_Pos) /*!< 0x00010000 */ +#define ADC_CR_ADCALLIN ADC_CR_ADCALLIN_Msk /*!< ADC Linearity calibration */ +#define ADC_CR_LINCALRDYW1_Pos (22U) +#define ADC_CR_LINCALRDYW1_Msk (0x1UL << ADC_CR_LINCALRDYW1_Pos) /*!< 0x00400000 */ +#define ADC_CR_LINCALRDYW1 ADC_CR_LINCALRDYW1_Msk /*!< ADC Linearity calibration ready Word 1 */ +#define ADC_CR_LINCALRDYW2_Pos (23U) +#define ADC_CR_LINCALRDYW2_Msk (0x1UL << ADC_CR_LINCALRDYW2_Pos) /*!< 0x00800000 */ +#define ADC_CR_LINCALRDYW2 ADC_CR_LINCALRDYW2_Msk /*!< ADC Linearity calibration ready Word 2 */ +#define ADC_CR_LINCALRDYW3_Pos (24U) +#define ADC_CR_LINCALRDYW3_Msk (0x1UL << ADC_CR_LINCALRDYW3_Pos) /*!< 0x01000000 */ +#define ADC_CR_LINCALRDYW3 ADC_CR_LINCALRDYW3_Msk /*!< ADC Linearity calibration ready Word 3 */ +#define ADC_CR_LINCALRDYW4_Pos (25U) +#define ADC_CR_LINCALRDYW4_Msk (0x1UL << ADC_CR_LINCALRDYW4_Pos) /*!< 0x02000000 */ +#define ADC_CR_LINCALRDYW4 ADC_CR_LINCALRDYW4_Msk /*!< ADC Linearity calibration ready Word 4 */ +#define ADC_CR_LINCALRDYW5_Pos (26U) +#define ADC_CR_LINCALRDYW5_Msk (0x1UL << ADC_CR_LINCALRDYW5_Pos) /*!< 0x04000000 */ +#define ADC_CR_LINCALRDYW5 ADC_CR_LINCALRDYW5_Msk /*!< ADC Linearity calibration ready Word 5 */ +#define ADC_CR_LINCALRDYW6_Pos (27U) +#define ADC_CR_LINCALRDYW6_Msk (0x1UL << ADC_CR_LINCALRDYW6_Pos) /*!< 0x08000000 */ +#define ADC_CR_LINCALRDYW6 ADC_CR_LINCALRDYW6_Msk /*!< ADC Linearity calibration ready Word 6 */ +#define ADC_CR_ADVREGEN_Pos (28U) +#define ADC_CR_ADVREGEN_Msk (0x1UL << ADC_CR_ADVREGEN_Pos) /*!< 0x10000000 */ +#define ADC_CR_ADVREGEN ADC_CR_ADVREGEN_Msk /*!< ADC Voltage regulator Enable */ +#define ADC_CR_DEEPPWD_Pos (29U) +#define ADC_CR_DEEPPWD_Msk (0x1UL << ADC_CR_DEEPPWD_Pos) /*!< 0x20000000 */ +#define ADC_CR_DEEPPWD ADC_CR_DEEPPWD_Msk /*!< ADC Deep power down Enable */ +#define ADC_CR_ADCALDIF_Pos (30U) +#define ADC_CR_ADCALDIF_Msk (0x1UL << ADC_CR_ADCALDIF_Pos) /*!< 0x40000000 */ +#define ADC_CR_ADCALDIF ADC_CR_ADCALDIF_Msk /*!< ADC Differential Mode for calibration */ +#define ADC_CR_ADCAL_Pos (31U) +#define ADC_CR_ADCAL_Msk (0x1UL << ADC_CR_ADCAL_Pos) /*!< 0x80000000 */ +#define ADC_CR_ADCAL ADC_CR_ADCAL_Msk /*!< ADC Calibration */ + +/******************** Bit definition for ADC_CFGR register ********************/ +#define ADC_CFGR_DMNGT_Pos (0U) +#define ADC_CFGR_DMNGT_Msk (0x3UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000003 */ +#define ADC_CFGR_DMNGT ADC_CFGR_DMNGT_Msk /*!< ADC Data Management configuration */ +#define ADC_CFGR_DMNGT_0 (0x1UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000001 */ +#define ADC_CFGR_DMNGT_1 (0x2UL << ADC_CFGR_DMNGT_Pos) /*!< 0x00000002 */ + +#define ADC_CFGR_RES_Pos (2U) +#define ADC_CFGR_RES_Msk (0x7UL << ADC_CFGR_RES_Pos) /*!< 0x0000001C */ +#define ADC_CFGR_RES ADC_CFGR_RES_Msk /*!< ADC Data resolution */ +#define ADC_CFGR_RES_0 (0x1UL << ADC_CFGR_RES_Pos) /*!< 0x00000004 */ +#define ADC_CFGR_RES_1 (0x2UL << ADC_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC_CFGR_RES_2 (0x4UL << ADC_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR_EXTSEL_Pos (5U) +#define ADC_CFGR_EXTSEL_Msk (0x1FUL << ADC_CFGR_EXTSEL_Pos) /*!< 0x000003E0 */ +#define ADC_CFGR_EXTSEL ADC_CFGR_EXTSEL_Msk /*!< ADC External trigger selection for regular group */ +#define ADC_CFGR_EXTSEL_0 (0x01UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_CFGR_EXTSEL_1 (0x02UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000040 */ +#define ADC_CFGR_EXTSEL_2 (0x04UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000080 */ +#define ADC_CFGR_EXTSEL_3 (0x08UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000100 */ +#define ADC_CFGR_EXTSEL_4 (0x10UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000200 */ + +#define ADC_CFGR_EXTEN_Pos (10U) +#define ADC_CFGR_EXTEN_Msk (0x3UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000C00 */ +#define ADC_CFGR_EXTEN ADC_CFGR_EXTEN_Msk /*!< ADC External trigger enable and polarity selection for regular channels */ +#define ADC_CFGR_EXTEN_0 (0x1UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000400 */ +#define ADC_CFGR_EXTEN_1 (0x2UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000800 */ + +#define ADC_CFGR_OVRMOD_Pos (12U) +#define ADC_CFGR_OVRMOD_Msk (0x1UL << ADC_CFGR_OVRMOD_Pos) /*!< 0x00001000 */ +#define ADC_CFGR_OVRMOD ADC_CFGR_OVRMOD_Msk /*!< ADC overrun mode */ +#define ADC_CFGR_CONT_Pos (13U) +#define ADC_CFGR_CONT_Msk (0x1UL << ADC_CFGR_CONT_Pos) /*!< 0x00002000 */ +#define ADC_CFGR_CONT ADC_CFGR_CONT_Msk /*!< ADC Single/continuous conversion mode for regular conversion */ +#define ADC_CFGR_AUTDLY_Pos (14U) +#define ADC_CFGR_AUTDLY_Msk (0x1UL << ADC_CFGR_AUTDLY_Pos) /*!< 0x00004000 */ +#define ADC_CFGR_AUTDLY ADC_CFGR_AUTDLY_Msk /*!< ADC Delayed conversion mode */ + +#define ADC_CFGR_DISCEN_Pos (16U) +#define ADC_CFGR_DISCEN_Msk (0x1UL << ADC_CFGR_DISCEN_Pos) /*!< 0x00010000 */ +#define ADC_CFGR_DISCEN ADC_CFGR_DISCEN_Msk /*!< ADC Discontinuous mode for regular channels */ + +#define ADC_CFGR_DISCNUM_Pos (17U) +#define ADC_CFGR_DISCNUM_Msk (0x7UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x000E0000 */ +#define ADC_CFGR_DISCNUM ADC_CFGR_DISCNUM_Msk /*!< ADC Discontinuous mode channel count */ +#define ADC_CFGR_DISCNUM_0 (0x1UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00020000 */ +#define ADC_CFGR_DISCNUM_1 (0x2UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00040000 */ +#define ADC_CFGR_DISCNUM_2 (0x4UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00080000 */ + +#define ADC_CFGR_JDISCEN_Pos (20U) +#define ADC_CFGR_JDISCEN_Msk (0x1UL << ADC_CFGR_JDISCEN_Pos) /*!< 0x00100000 */ +#define ADC_CFGR_JDISCEN ADC_CFGR_JDISCEN_Msk /*!< ADC Discontinuous mode on injected channels */ +#define ADC_CFGR_JQM_Pos (21U) +#define ADC_CFGR_JQM_Msk (0x1UL << ADC_CFGR_JQM_Pos) /*!< 0x00200000 */ +#define ADC_CFGR_JQM ADC_CFGR_JQM_Msk /*!< ADC JSQR Queue mode */ +#define ADC_CFGR_AWD1SGL_Pos (22U) +#define ADC_CFGR_AWD1SGL_Msk (0x1UL << ADC_CFGR_AWD1SGL_Pos) /*!< 0x00400000 */ +#define ADC_CFGR_AWD1SGL ADC_CFGR_AWD1SGL_Msk /*!< Enable the watchdog 1 on a single channel or on all channels */ +#define ADC_CFGR_AWD1EN_Pos (23U) +#define ADC_CFGR_AWD1EN_Msk (0x1UL << ADC_CFGR_AWD1EN_Pos) /*!< 0x00800000 */ +#define ADC_CFGR_AWD1EN ADC_CFGR_AWD1EN_Msk /*!< ADC Analog watchdog 1 enable on regular Channels */ +#define ADC_CFGR_JAWD1EN_Pos (24U) +#define ADC_CFGR_JAWD1EN_Msk (0x1UL << ADC_CFGR_JAWD1EN_Pos) /*!< 0x01000000 */ +#define ADC_CFGR_JAWD1EN ADC_CFGR_JAWD1EN_Msk /*!< ADC Analog watchdog 1 enable on injected Channels */ +#define ADC_CFGR_JAUTO_Pos (25U) +#define ADC_CFGR_JAUTO_Msk (0x1UL << ADC_CFGR_JAUTO_Pos) /*!< 0x02000000 */ +#define ADC_CFGR_JAUTO ADC_CFGR_JAUTO_Msk /*!< ADC Automatic injected group conversion */ + +#define ADC_CFGR_AWD1CH_Pos (26U) +#define ADC_CFGR_AWD1CH_Msk (0x1FUL << ADC_CFGR_AWD1CH_Pos) /*!< 0x7C000000 */ +#define ADC_CFGR_AWD1CH ADC_CFGR_AWD1CH_Msk /*!< ADC Analog watchdog 1 Channel selection */ +#define ADC_CFGR_AWD1CH_0 (0x01UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x04000000 */ +#define ADC_CFGR_AWD1CH_1 (0x02UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x08000000 */ +#define ADC_CFGR_AWD1CH_2 (0x04UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x10000000 */ +#define ADC_CFGR_AWD1CH_3 (0x08UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x20000000 */ +#define ADC_CFGR_AWD1CH_4 (0x10UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x40000000 */ + +#define ADC_CFGR_JQDIS_Pos (31U) +#define ADC_CFGR_JQDIS_Msk (0x1UL << ADC_CFGR_JQDIS_Pos) /*!< 0x80000000 */ +#define ADC_CFGR_JQDIS ADC_CFGR_JQDIS_Msk /*!< ADC Injected queue disable */ + +#define ADC3_CFGR_DMAEN_Pos (0U) +#define ADC3_CFGR_DMAEN_Msk (0x1UL << ADC3_CFGR_DMAEN_Pos) /*!< 0x00000001 */ +#define ADC3_CFGR_DMAEN ADC3_CFGR_DMAEN_Msk /*!< ADC DMA transfer enable */ +#define ADC3_CFGR_DMACFG_Pos (1U) +#define ADC3_CFGR_DMACFG_Msk (0x1UL << ADC3_CFGR_DMACFG_Pos) /*!< 0x00000002 */ +#define ADC3_CFGR_DMACFG ADC3_CFGR_DMACFG_Msk /*!< ADC DMA transfer configuration */ + +#define ADC3_CFGR_RES_Pos (3U) +#define ADC3_CFGR_RES_Msk (0x3UL << ADC3_CFGR_RES_Pos) /*!< 0x00000018 */ +#define ADC3_CFGR_RES ADC3_CFGR_RES_Msk /*!< ADC data resolution */ +#define ADC3_CFGR_RES_0 (0x1UL << ADC3_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC3_CFGR_RES_1 (0x2UL << ADC3_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC3_CFGR_ALIGN_Pos (15U) +#define ADC3_CFGR_ALIGN_Msk (0x1UL << ADC3_CFGR_ALIGN_Pos) /*!< 0x00008000 */ +#define ADC3_CFGR_ALIGN ADC3_CFGR_ALIGN_Msk /*!< ADC data alignment */ +/******************** Bit definition for ADC_CFGR2 register ********************/ +#define ADC_CFGR2_ROVSE_Pos (0U) +#define ADC_CFGR2_ROVSE_Msk (0x1UL << ADC_CFGR2_ROVSE_Pos) /*!< 0x00000001 */ +#define ADC_CFGR2_ROVSE ADC_CFGR2_ROVSE_Msk /*!< ADC Regular group oversampler enable */ +#define ADC_CFGR2_JOVSE_Pos (1U) +#define ADC_CFGR2_JOVSE_Msk (0x1UL << ADC_CFGR2_JOVSE_Pos) /*!< 0x00000002 */ +#define ADC_CFGR2_JOVSE ADC_CFGR2_JOVSE_Msk /*!< ADC Injected group oversampler enable */ + +#define ADC_CFGR2_OVSS_Pos (5U) +#define ADC_CFGR2_OVSS_Msk (0xFUL << ADC_CFGR2_OVSS_Pos) /*!< 0x000001E0 */ +#define ADC_CFGR2_OVSS ADC_CFGR2_OVSS_Msk /*!< ADC Regular Oversampling shift */ +#define ADC_CFGR2_OVSS_0 (0x1UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000020 */ +#define ADC_CFGR2_OVSS_1 (0x2UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000040 */ +#define ADC_CFGR2_OVSS_2 (0x4UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000080 */ +#define ADC_CFGR2_OVSS_3 (0x8UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000100 */ + +#define ADC_CFGR2_TROVS_Pos (9U) +#define ADC_CFGR2_TROVS_Msk (0x1UL << ADC_CFGR2_TROVS_Pos) /*!< 0x00000200 */ +#define ADC_CFGR2_TROVS ADC_CFGR2_TROVS_Msk /*!< ADC Triggered regular Oversampling */ +#define ADC_CFGR2_ROVSM_Pos (10U) +#define ADC_CFGR2_ROVSM_Msk (0x1UL << ADC_CFGR2_ROVSM_Pos) /*!< 0x00000400 */ +#define ADC_CFGR2_ROVSM ADC_CFGR2_ROVSM_Msk /*!< ADC Regular oversampling mode */ + +#define ADC_CFGR2_RSHIFT1_Pos (11U) +#define ADC_CFGR2_RSHIFT1_Msk (0x1UL << ADC_CFGR2_RSHIFT1_Pos) /*!< 0x00000800 */ +#define ADC_CFGR2_RSHIFT1 ADC_CFGR2_RSHIFT1_Msk /*!< ADC Right-shift data after Offset 1 correction */ +#define ADC_CFGR2_RSHIFT2_Pos (12U) +#define ADC_CFGR2_RSHIFT2_Msk (0x1UL << ADC_CFGR2_RSHIFT2_Pos) /*!< 0x00001000 */ +#define ADC_CFGR2_RSHIFT2 ADC_CFGR2_RSHIFT2_Msk /*!< ADC Right-shift data after Offset 2 correction */ +#define ADC_CFGR2_RSHIFT3_Pos (13U) +#define ADC_CFGR2_RSHIFT3_Msk (0x1UL << ADC_CFGR2_RSHIFT3_Pos) /*!< 0x00002000 */ +#define ADC_CFGR2_RSHIFT3 ADC_CFGR2_RSHIFT3_Msk /*!< ADC Right-shift data after Offset 3 correction */ +#define ADC_CFGR2_RSHIFT4_Pos (14U) +#define ADC_CFGR2_RSHIFT4_Msk (0x1UL << ADC_CFGR2_RSHIFT4_Pos) /*!< 0x00004000 */ +#define ADC_CFGR2_RSHIFT4 ADC_CFGR2_RSHIFT4_Msk /*!< ADC Right-shift data after Offset 4 correction */ + +#define ADC_CFGR2_OVSR_Pos (16U) +#define ADC_CFGR2_OVSR_Msk (0x3FFUL << ADC_CFGR2_OVSR_Pos) /*!< 0x03FF0000 */ +#define ADC_CFGR2_OVSR ADC_CFGR2_OVSR_Msk /*!< ADC oversampling Ratio */ +#define ADC_CFGR2_OVSR_0 (0x001UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00010000 */ +#define ADC_CFGR2_OVSR_1 (0x002UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00020000 */ +#define ADC_CFGR2_OVSR_2 (0x004UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00040000 */ +#define ADC_CFGR2_OVSR_3 (0x008UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00080000 */ +#define ADC_CFGR2_OVSR_4 (0x010UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00100000 */ +#define ADC_CFGR2_OVSR_5 (0x020UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00200000 */ +#define ADC_CFGR2_OVSR_6 (0x040UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00400000 */ +#define ADC_CFGR2_OVSR_7 (0x080UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00800000 */ +#define ADC_CFGR2_OVSR_8 (0x100UL << ADC_CFGR2_OVSR_Pos) /*!< 0x01000000 */ +#define ADC_CFGR2_OVSR_9 (0x200UL << ADC_CFGR2_OVSR_Pos) /*!< 0x02000000 */ + +#define ADC_CFGR2_LSHIFT_Pos (28U) +#define ADC_CFGR2_LSHIFT_Msk (0xFUL << ADC_CFGR2_LSHIFT_Pos) /*!< 0xF0000000 */ +#define ADC_CFGR2_LSHIFT ADC_CFGR2_LSHIFT_Msk /*!< ADC Left shift factor */ +#define ADC_CFGR2_LSHIFT_0 (0x1UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x10000000 */ +#define ADC_CFGR2_LSHIFT_1 (0x2UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x20000000 */ +#define ADC_CFGR2_LSHIFT_2 (0x4UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x40000000 */ +#define ADC_CFGR2_LSHIFT_3 (0x8UL << ADC_CFGR2_LSHIFT_Pos) /*!< 0x80000000 */ + +#define ADC3_CFGR2_OVSR_Pos (2U) +#define ADC3_CFGR2_OVSR_Msk (0x7UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x0000001C */ +#define ADC3_CFGR2_OVSR ADC3_CFGR2_OVSR_Msk /*!< ADC oversampling ratio */ +#define ADC3_CFGR2_OVSR_0 (0x1UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000004 */ +#define ADC3_CFGR2_OVSR_1 (0x2UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000008 */ +#define ADC3_CFGR2_OVSR_2 (0x4UL << ADC3_CFGR2_OVSR_Pos) /*!< 0x00000010 */ + +#define ADC3_CFGR2_SWTRIG_Pos (25U) +#define ADC3_CFGR2_SWTRIG_Msk (0x1UL << ADC3_CFGR2_SWTRIG_Pos) /*!< 0x02000000 */ +#define ADC3_CFGR2_SWTRIG ADC3_CFGR2_SWTRIG_Msk /*!< ADC Software Trigger Bit for Sample time control trigger mode */ +#define ADC3_CFGR2_BULB_Pos (26U) +#define ADC3_CFGR2_BULB_Msk (0x1UL << ADC3_CFGR2_BULB_Pos) /*!< 0x04000000 */ +#define ADC3_CFGR2_BULB ADC3_CFGR2_BULB_Msk /*!< ADC Bulb sampling mode */ +#define ADC3_CFGR2_SMPTRIG_Pos (27U) +#define ADC3_CFGR2_SMPTRIG_Msk (0x1UL << ADC3_CFGR2_SMPTRIG_Pos) /*!< 0x08000000 */ +#define ADC3_CFGR2_SMPTRIG ADC3_CFGR2_SMPTRIG_Msk /*!< ADC Sample Time Control Trigger mode */ +/******************** Bit definition for ADC_SMPR1 register ********************/ +#define ADC_SMPR1_SMP0_Pos (0U) +#define ADC_SMPR1_SMP0_Msk (0x7UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000007 */ +#define ADC_SMPR1_SMP0 ADC_SMPR1_SMP0_Msk /*!< ADC Channel 0 Sampling time selection */ +#define ADC_SMPR1_SMP0_0 (0x1UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000001 */ +#define ADC_SMPR1_SMP0_1 (0x2UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000002 */ +#define ADC_SMPR1_SMP0_2 (0x4UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR1_SMP1_Pos (3U) +#define ADC_SMPR1_SMP1_Msk (0x7UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000038 */ +#define ADC_SMPR1_SMP1 ADC_SMPR1_SMP1_Msk /*!< ADC Channel 1 Sampling time selection */ +#define ADC_SMPR1_SMP1_0 (0x1UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000008 */ +#define ADC_SMPR1_SMP1_1 (0x2UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000010 */ +#define ADC_SMPR1_SMP1_2 (0x4UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR1_SMP2_Pos (6U) +#define ADC_SMPR1_SMP2_Msk (0x7UL << ADC_SMPR1_SMP2_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR1_SMP2 ADC_SMPR1_SMP2_Msk /*!< ADC Channel 2 Sampling time selection */ +#define ADC_SMPR1_SMP2_0 (0x1UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000040 */ +#define ADC_SMPR1_SMP2_1 (0x2UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000080 */ +#define ADC_SMPR1_SMP2_2 (0x4UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR1_SMP3_Pos (9U) +#define ADC_SMPR1_SMP3_Msk (0x7UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR1_SMP3 ADC_SMPR1_SMP3_Msk /*!< ADC Channel 3 Sampling time selection */ +#define ADC_SMPR1_SMP3_0 (0x1UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000200 */ +#define ADC_SMPR1_SMP3_1 (0x2UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000400 */ +#define ADC_SMPR1_SMP3_2 (0x4UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR1_SMP4_Pos (12U) +#define ADC_SMPR1_SMP4_Msk (0x7UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00007000 */ +#define ADC_SMPR1_SMP4 ADC_SMPR1_SMP4_Msk /*!< ADC Channel 4 Sampling time selection */ +#define ADC_SMPR1_SMP4_0 (0x1UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00001000 */ +#define ADC_SMPR1_SMP4_1 (0x2UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00002000 */ +#define ADC_SMPR1_SMP4_2 (0x4UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR1_SMP5_Pos (15U) +#define ADC_SMPR1_SMP5_Msk (0x7UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00038000 */ +#define ADC_SMPR1_SMP5 ADC_SMPR1_SMP5_Msk /*!< ADC Channel 5 Sampling time selection */ +#define ADC_SMPR1_SMP5_0 (0x1UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00008000 */ +#define ADC_SMPR1_SMP5_1 (0x2UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00010000 */ +#define ADC_SMPR1_SMP5_2 (0x4UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR1_SMP6_Pos (18U) +#define ADC_SMPR1_SMP6_Msk (0x7UL << ADC_SMPR1_SMP6_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR1_SMP6 ADC_SMPR1_SMP6_Msk /*!< ADC Channel 6 Sampling time selection */ +#define ADC_SMPR1_SMP6_0 (0x1UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00040000 */ +#define ADC_SMPR1_SMP6_1 (0x2UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00080000 */ +#define ADC_SMPR1_SMP6_2 (0x4UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR1_SMP7_Pos (21U) +#define ADC_SMPR1_SMP7_Msk (0x7UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR1_SMP7 ADC_SMPR1_SMP7_Msk /*!< ADC Channel 7 Sampling time selection */ +#define ADC_SMPR1_SMP7_0 (0x1UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00200000 */ +#define ADC_SMPR1_SMP7_1 (0x2UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00400000 */ +#define ADC_SMPR1_SMP7_2 (0x4UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR1_SMP8_Pos (24U) +#define ADC_SMPR1_SMP8_Msk (0x7UL << ADC_SMPR1_SMP8_Pos) /*!< 0x07000000 */ +#define ADC_SMPR1_SMP8 ADC_SMPR1_SMP8_Msk /*!< ADC Channel 8 Sampling time selection */ +#define ADC_SMPR1_SMP8_0 (0x1UL << ADC_SMPR1_SMP8_Pos) /*!< 0x01000000 */ +#define ADC_SMPR1_SMP8_1 (0x2UL << ADC_SMPR1_SMP8_Pos) /*!< 0x02000000 */ +#define ADC_SMPR1_SMP8_2 (0x4UL << ADC_SMPR1_SMP8_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR1_SMP9_Pos (27U) +#define ADC_SMPR1_SMP9_Msk (0x7UL << ADC_SMPR1_SMP9_Pos) /*!< 0x38000000 */ +#define ADC_SMPR1_SMP9 ADC_SMPR1_SMP9_Msk /*!< ADC Channel 9 Sampling time selection */ +#define ADC_SMPR1_SMP9_0 (0x1UL << ADC_SMPR1_SMP9_Pos) /*!< 0x08000000 */ +#define ADC_SMPR1_SMP9_1 (0x2UL << ADC_SMPR1_SMP9_Pos) /*!< 0x10000000 */ +#define ADC_SMPR1_SMP9_2 (0x4UL << ADC_SMPR1_SMP9_Pos) /*!< 0x20000000 */ + +/******************** Bit definition for ADC_SMPR2 register ********************/ +#define ADC_SMPR2_SMP10_Pos (0U) +#define ADC_SMPR2_SMP10_Msk (0x7UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000007 */ +#define ADC_SMPR2_SMP10 ADC_SMPR2_SMP10_Msk /*!< ADC Channel 10 Sampling time selection */ +#define ADC_SMPR2_SMP10_0 (0x1UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000001 */ +#define ADC_SMPR2_SMP10_1 (0x2UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000002 */ +#define ADC_SMPR2_SMP10_2 (0x4UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR2_SMP11_Pos (3U) +#define ADC_SMPR2_SMP11_Msk (0x7UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000038 */ +#define ADC_SMPR2_SMP11 ADC_SMPR2_SMP11_Msk /*!< ADC Channel 11 Sampling time selection */ +#define ADC_SMPR2_SMP11_0 (0x1UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000008 */ +#define ADC_SMPR2_SMP11_1 (0x2UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000010 */ +#define ADC_SMPR2_SMP11_2 (0x4UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR2_SMP12_Pos (6U) +#define ADC_SMPR2_SMP12_Msk (0x7UL << ADC_SMPR2_SMP12_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR2_SMP12 ADC_SMPR2_SMP12_Msk /*!< ADC Channel 12 Sampling time selection */ +#define ADC_SMPR2_SMP12_0 (0x1UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000040 */ +#define ADC_SMPR2_SMP12_1 (0x2UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000080 */ +#define ADC_SMPR2_SMP12_2 (0x4UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR2_SMP13_Pos (9U) +#define ADC_SMPR2_SMP13_Msk (0x7UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR2_SMP13 ADC_SMPR2_SMP13_Msk /*!< ADC Channel 13 Sampling time selection */ +#define ADC_SMPR2_SMP13_0 (0x1UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000200 */ +#define ADC_SMPR2_SMP13_1 (0x2UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000400 */ +#define ADC_SMPR2_SMP13_2 (0x4UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR2_SMP14_Pos (12U) +#define ADC_SMPR2_SMP14_Msk (0x7UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00007000 */ +#define ADC_SMPR2_SMP14 ADC_SMPR2_SMP14_Msk /*!< ADC Channel 14 Sampling time selection */ +#define ADC_SMPR2_SMP14_0 (0x1UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00001000 */ +#define ADC_SMPR2_SMP14_1 (0x2UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00002000 */ +#define ADC_SMPR2_SMP14_2 (0x4UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR2_SMP15_Pos (15U) +#define ADC_SMPR2_SMP15_Msk (0x7UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00038000 */ +#define ADC_SMPR2_SMP15 ADC_SMPR2_SMP15_Msk /*!< ADC Channel 15 Sampling time selection */ +#define ADC_SMPR2_SMP15_0 (0x1UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00008000 */ +#define ADC_SMPR2_SMP15_1 (0x2UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00010000 */ +#define ADC_SMPR2_SMP15_2 (0x4UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR2_SMP16_Pos (18U) +#define ADC_SMPR2_SMP16_Msk (0x7UL << ADC_SMPR2_SMP16_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR2_SMP16 ADC_SMPR2_SMP16_Msk /*!< ADC Channel 16 Sampling time selection */ +#define ADC_SMPR2_SMP16_0 (0x1UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00040000 */ +#define ADC_SMPR2_SMP16_1 (0x2UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00080000 */ +#define ADC_SMPR2_SMP16_2 (0x4UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR2_SMP17_Pos (21U) +#define ADC_SMPR2_SMP17_Msk (0x7UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR2_SMP17 ADC_SMPR2_SMP17_Msk /*!< ADC Channel 17 Sampling time selection */ +#define ADC_SMPR2_SMP17_0 (0x1UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00200000 */ +#define ADC_SMPR2_SMP17_1 (0x2UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00400000 */ +#define ADC_SMPR2_SMP17_2 (0x4UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR2_SMP18_Pos (24U) +#define ADC_SMPR2_SMP18_Msk (0x7UL << ADC_SMPR2_SMP18_Pos) /*!< 0x07000000 */ +#define ADC_SMPR2_SMP18 ADC_SMPR2_SMP18_Msk /*!< ADC Channel 18 Sampling time selection */ +#define ADC_SMPR2_SMP18_0 (0x1UL << ADC_SMPR2_SMP18_Pos) /*!< 0x01000000 */ +#define ADC_SMPR2_SMP18_1 (0x2UL << ADC_SMPR2_SMP18_Pos) /*!< 0x02000000 */ +#define ADC_SMPR2_SMP18_2 (0x4UL << ADC_SMPR2_SMP18_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR2_SMP19_Pos (27U) +#define ADC_SMPR2_SMP19_Msk (0x7UL << ADC_SMPR2_SMP19_Pos) /*!< 0x38000000 */ +#define ADC_SMPR2_SMP19 ADC_SMPR2_SMP19_Msk /*!< ADC Channel 19 Sampling time selection */ +#define ADC_SMPR2_SMP19_0 (0x1UL << ADC_SMPR2_SMP19_Pos) /*!< 0x08000000 */ +#define ADC_SMPR2_SMP19_1 (0x2UL << ADC_SMPR2_SMP19_Pos) /*!< 0x10000000 */ +#define ADC_SMPR2_SMP19_2 (0x4UL << ADC_SMPR2_SMP19_Pos) /*!< 0x20000000 */ + +/******************** Bit definition for ADC_PCSEL register ********************/ +#define ADC_PCSEL_PCSEL_Pos (0U) +#define ADC_PCSEL_PCSEL_Msk (0xFFFFFUL << ADC_PCSEL_PCSEL_Pos) /*!< 0x000FFFFF */ +#define ADC_PCSEL_PCSEL ADC_PCSEL_PCSEL_Msk /*!< ADC pre channel selection */ +#define ADC_PCSEL_PCSEL_0 (0x00001UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000001 */ +#define ADC_PCSEL_PCSEL_1 (0x00002UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000002 */ +#define ADC_PCSEL_PCSEL_2 (0x00004UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000004 */ +#define ADC_PCSEL_PCSEL_3 (0x00008UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000008 */ +#define ADC_PCSEL_PCSEL_4 (0x00010UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000010 */ +#define ADC_PCSEL_PCSEL_5 (0x00020UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000020 */ +#define ADC_PCSEL_PCSEL_6 (0x00040UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000040 */ +#define ADC_PCSEL_PCSEL_7 (0x00080UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000080 */ +#define ADC_PCSEL_PCSEL_8 (0x00100UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000100 */ +#define ADC_PCSEL_PCSEL_9 (0x00200UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000200 */ +#define ADC_PCSEL_PCSEL_10 (0x00400UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000400 */ +#define ADC_PCSEL_PCSEL_11 (0x00800UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00000800 */ +#define ADC_PCSEL_PCSEL_12 (0x01000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00001000 */ +#define ADC_PCSEL_PCSEL_13 (0x02000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00002000 */ +#define ADC_PCSEL_PCSEL_14 (0x04000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00004000 */ +#define ADC_PCSEL_PCSEL_15 (0x08000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00008000 */ +#define ADC_PCSEL_PCSEL_16 (0x10000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00010000 */ +#define ADC_PCSEL_PCSEL_17 (0x20000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00020000 */ +#define ADC_PCSEL_PCSEL_18 (0x40000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00040000 */ +#define ADC_PCSEL_PCSEL_19 (0x80000UL << ADC_PCSEL_PCSEL_Pos) /*!< 0x00080000 */ + +/***************** Bit definition for ADC_LTR1, 2, 3 registers *****************/ +#define ADC_LTR_LT_Pos (0U) +#define ADC_LTR_LT_Msk (0x3FFFFFFUL << ADC_LTR_LT_Pos) /*!< 0x03FFFFFF */ +#define ADC_LTR_LT ADC_LTR_LT_Msk /*!< ADC Analog watchdog 1, 2 and 3 lower threshold */ + +/***************** Bit definition for ADC_HTR1, 2, 3 registers ****************/ +#define ADC_HTR_HT_Pos (0U) +#define ADC_HTR_HT_Msk (0x3FFFFFFUL << ADC_HTR_HT_Pos) /*!< 0x03FFFFFF */ +#define ADC_HTR_HT ADC_HTR_HT_Msk /*!< ADC Analog watchdog 1,2 and 3 higher threshold */ + +/******************** Bit definition for ADC3_TR1 register *******************/ +#define ADC3_TR1_LT1_Pos (0U) +#define ADC3_TR1_LT1_Msk (0xFFFUL << ADC3_TR1_LT1_Pos) /*!< 0x00000FFF */ +#define ADC3_TR1_LT1 ADC3_TR1_LT1_Msk /*!< ADC analog watchdog 1 threshold low */ + +#define ADC3_TR1_AWDFILT_Pos (12U) +#define ADC3_TR1_AWDFILT_Msk (0x7UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00007000 */ +#define ADC3_TR1_AWDFILT ADC3_TR1_AWDFILT_Msk /*!< ADC analog watchdog filtering parameter */ +#define ADC3_TR1_AWDFILT_0 (0x1UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00001000 */ +#define ADC3_TR1_AWDFILT_1 (0x2UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00002000 */ +#define ADC3_TR1_AWDFILT_2 (0x4UL << ADC3_TR1_AWDFILT_Pos) /*!< 0x00004000 */ + +#define ADC3_TR1_HT1_Pos (16U) +#define ADC3_TR1_HT1_Msk (0xFFFUL << ADC3_TR1_HT1_Pos) /*!< 0x0FFF0000 */ +#define ADC3_TR1_HT1 ADC3_TR1_HT1_Msk /*!< ADC analog watchdog 1 threshold high */ + +/******************** Bit definition for ADC3_TR2 register *******************/ +#define ADC3_TR2_LT2_Pos (0U) +#define ADC3_TR2_LT2_Msk (0xFFUL << ADC3_TR2_LT2_Pos) /*!< 0x000000FF */ +#define ADC3_TR2_LT2 ADC3_TR2_LT2_Msk /*!< ADC analog watchdog 2 threshold low */ + +#define ADC3_TR2_HT2_Pos (16U) +#define ADC3_TR2_HT2_Msk (0xFFUL << ADC3_TR2_HT2_Pos) /*!< 0x00FF0000 */ +#define ADC3_TR2_HT2 ADC3_TR2_HT2_Msk /*!< ADC analog watchdog 2 threshold high */ + +/******************** Bit definition for ADC3_TR3 register *******************/ +#define ADC3_TR3_LT3_Pos (0U) +#define ADC3_TR3_LT3_Msk (0xFFUL << ADC3_TR3_LT3_Pos) /*!< 0x000000FF */ +#define ADC3_TR3_LT3 ADC3_TR3_LT3_Msk /*!< ADC analog watchdog 3 threshold low */ + +#define ADC3_TR3_HT3_Pos (16U) +#define ADC3_TR3_HT3_Msk (0xFFUL << ADC3_TR3_HT3_Pos) /*!< 0x00FF0000 */ +#define ADC3_TR3_HT3 ADC3_TR3_HT3_Msk /*!< ADC analog watchdog 3 threshold high */ + +/******************** Bit definition for ADC_SQR1 register ********************/ +#define ADC_SQR1_L_Pos (0U) +#define ADC_SQR1_L_Msk (0xFUL << ADC_SQR1_L_Pos) /*!< 0x0000000F */ +#define ADC_SQR1_L ADC_SQR1_L_Msk /*!< ADC regular channel sequence length */ +#define ADC_SQR1_L_0 (0x1UL << ADC_SQR1_L_Pos) /*!< 0x00000001 */ +#define ADC_SQR1_L_1 (0x2UL << ADC_SQR1_L_Pos) /*!< 0x00000002 */ +#define ADC_SQR1_L_2 (0x4UL << ADC_SQR1_L_Pos) /*!< 0x00000004 */ +#define ADC_SQR1_L_3 (0x8UL << ADC_SQR1_L_Pos) /*!< 0x00000008 */ + +#define ADC_SQR1_SQ1_Pos (6U) +#define ADC_SQR1_SQ1_Msk (0x1FUL << ADC_SQR1_SQ1_Pos) /*!< 0x000007C0 */ +#define ADC_SQR1_SQ1 ADC_SQR1_SQ1_Msk /*!< ADC 1st conversion in regular sequence */ +#define ADC_SQR1_SQ1_0 (0x01UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000040 */ +#define ADC_SQR1_SQ1_1 (0x02UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000080 */ +#define ADC_SQR1_SQ1_2 (0x04UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000100 */ +#define ADC_SQR1_SQ1_3 (0x08UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000200 */ +#define ADC_SQR1_SQ1_4 (0x10UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000400 */ + +#define ADC_SQR1_SQ2_Pos (12U) +#define ADC_SQR1_SQ2_Msk (0x1FUL << ADC_SQR1_SQ2_Pos) /*!< 0x0001F000 */ +#define ADC_SQR1_SQ2 ADC_SQR1_SQ2_Msk /*!< ADC 2nd conversion in regular sequence */ +#define ADC_SQR1_SQ2_0 (0x01UL << ADC_SQR1_SQ2_Pos) /*!< 0x00001000 */ +#define ADC_SQR1_SQ2_1 (0x02UL << ADC_SQR1_SQ2_Pos) /*!< 0x00002000 */ +#define ADC_SQR1_SQ2_2 (0x04UL << ADC_SQR1_SQ2_Pos) /*!< 0x00004000 */ +#define ADC_SQR1_SQ2_3 (0x08UL << ADC_SQR1_SQ2_Pos) /*!< 0x00008000 */ +#define ADC_SQR1_SQ2_4 (0x10UL << ADC_SQR1_SQ2_Pos) /*!< 0x00010000 */ + +#define ADC_SQR1_SQ3_Pos (18U) +#define ADC_SQR1_SQ3_Msk (0x1FUL << ADC_SQR1_SQ3_Pos) /*!< 0x007C0000 */ +#define ADC_SQR1_SQ3 ADC_SQR1_SQ3_Msk /*!< ADC 3rd conversion in regular sequence */ +#define ADC_SQR1_SQ3_0 (0x01UL << ADC_SQR1_SQ3_Pos) /*!< 0x00040000 */ +#define ADC_SQR1_SQ3_1 (0x02UL << ADC_SQR1_SQ3_Pos) /*!< 0x00080000 */ +#define ADC_SQR1_SQ3_2 (0x04UL << ADC_SQR1_SQ3_Pos) /*!< 0x00100000 */ +#define ADC_SQR1_SQ3_3 (0x08UL << ADC_SQR1_SQ3_Pos) /*!< 0x00200000 */ +#define ADC_SQR1_SQ3_4 (0x10UL << ADC_SQR1_SQ3_Pos) /*!< 0x00400000 */ + +#define ADC_SQR1_SQ4_Pos (24U) +#define ADC_SQR1_SQ4_Msk (0x1FUL << ADC_SQR1_SQ4_Pos) /*!< 0x1F000000 */ +#define ADC_SQR1_SQ4 ADC_SQR1_SQ4_Msk /*!< ADC 4th conversion in regular sequence */ +#define ADC_SQR1_SQ4_0 (0x01UL << ADC_SQR1_SQ4_Pos) /*!< 0x01000000 */ +#define ADC_SQR1_SQ4_1 (0x02UL << ADC_SQR1_SQ4_Pos) /*!< 0x02000000 */ +#define ADC_SQR1_SQ4_2 (0x04UL << ADC_SQR1_SQ4_Pos) /*!< 0x04000000 */ +#define ADC_SQR1_SQ4_3 (0x08UL << ADC_SQR1_SQ4_Pos) /*!< 0x08000000 */ +#define ADC_SQR1_SQ4_4 (0x10UL << ADC_SQR1_SQ4_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR2 register ********************/ +#define ADC_SQR2_SQ5_Pos (0U) +#define ADC_SQR2_SQ5_Msk (0x1FUL << ADC_SQR2_SQ5_Pos) /*!< 0x0000001F */ +#define ADC_SQR2_SQ5 ADC_SQR2_SQ5_Msk /*!< ADC 5th conversion in regular sequence */ +#define ADC_SQR2_SQ5_0 (0x01UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000001 */ +#define ADC_SQR2_SQ5_1 (0x02UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000002 */ +#define ADC_SQR2_SQ5_2 (0x04UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000004 */ +#define ADC_SQR2_SQ5_3 (0x08UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000008 */ +#define ADC_SQR2_SQ5_4 (0x10UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000010 */ + +#define ADC_SQR2_SQ6_Pos (6U) +#define ADC_SQR2_SQ6_Msk (0x1FUL << ADC_SQR2_SQ6_Pos) /*!< 0x000007C0 */ +#define ADC_SQR2_SQ6 ADC_SQR2_SQ6_Msk /*!< ADC 6th conversion in regular sequence */ +#define ADC_SQR2_SQ6_0 (0x01UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000040 */ +#define ADC_SQR2_SQ6_1 (0x02UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000080 */ +#define ADC_SQR2_SQ6_2 (0x04UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000100 */ +#define ADC_SQR2_SQ6_3 (0x08UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000200 */ +#define ADC_SQR2_SQ6_4 (0x10UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000400 */ + +#define ADC_SQR2_SQ7_Pos (12U) +#define ADC_SQR2_SQ7_Msk (0x1FUL << ADC_SQR2_SQ7_Pos) /*!< 0x0001F000 */ +#define ADC_SQR2_SQ7 ADC_SQR2_SQ7_Msk /*!< ADC 7th conversion in regular sequence */ +#define ADC_SQR2_SQ7_0 (0x01UL << ADC_SQR2_SQ7_Pos) /*!< 0x00001000 */ +#define ADC_SQR2_SQ7_1 (0x02UL << ADC_SQR2_SQ7_Pos) /*!< 0x00002000 */ +#define ADC_SQR2_SQ7_2 (0x04UL << ADC_SQR2_SQ7_Pos) /*!< 0x00004000 */ +#define ADC_SQR2_SQ7_3 (0x08UL << ADC_SQR2_SQ7_Pos) /*!< 0x00008000 */ +#define ADC_SQR2_SQ7_4 (0x10UL << ADC_SQR2_SQ7_Pos) /*!< 0x00010000 */ + +#define ADC_SQR2_SQ8_Pos (18U) +#define ADC_SQR2_SQ8_Msk (0x1FUL << ADC_SQR2_SQ8_Pos) /*!< 0x007C0000 */ +#define ADC_SQR2_SQ8 ADC_SQR2_SQ8_Msk /*!< ADC 8th conversion in regular sequence */ +#define ADC_SQR2_SQ8_0 (0x01UL << ADC_SQR2_SQ8_Pos) /*!< 0x00040000 */ +#define ADC_SQR2_SQ8_1 (0x02UL << ADC_SQR2_SQ8_Pos) /*!< 0x00080000 */ +#define ADC_SQR2_SQ8_2 (0x04UL << ADC_SQR2_SQ8_Pos) /*!< 0x00100000 */ +#define ADC_SQR2_SQ8_3 (0x08UL << ADC_SQR2_SQ8_Pos) /*!< 0x00200000 */ +#define ADC_SQR2_SQ8_4 (0x10UL << ADC_SQR2_SQ8_Pos) /*!< 0x00400000 */ + +#define ADC_SQR2_SQ9_Pos (24U) +#define ADC_SQR2_SQ9_Msk (0x1FUL << ADC_SQR2_SQ9_Pos) /*!< 0x1F000000 */ +#define ADC_SQR2_SQ9 ADC_SQR2_SQ9_Msk /*!< ADC 9th conversion in regular sequence */ +#define ADC_SQR2_SQ9_0 (0x01UL << ADC_SQR2_SQ9_Pos) /*!< 0x01000000 */ +#define ADC_SQR2_SQ9_1 (0x02UL << ADC_SQR2_SQ9_Pos) /*!< 0x02000000 */ +#define ADC_SQR2_SQ9_2 (0x04UL << ADC_SQR2_SQ9_Pos) /*!< 0x04000000 */ +#define ADC_SQR2_SQ9_3 (0x08UL << ADC_SQR2_SQ9_Pos) /*!< 0x08000000 */ +#define ADC_SQR2_SQ9_4 (0x10UL << ADC_SQR2_SQ9_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR3 register ********************/ +#define ADC_SQR3_SQ10_Pos (0U) +#define ADC_SQR3_SQ10_Msk (0x1FUL << ADC_SQR3_SQ10_Pos) /*!< 0x0000001F */ +#define ADC_SQR3_SQ10 ADC_SQR3_SQ10_Msk /*!< ADC 10th conversion in regular sequence */ +#define ADC_SQR3_SQ10_0 (0x01UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000001 */ +#define ADC_SQR3_SQ10_1 (0x02UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000002 */ +#define ADC_SQR3_SQ10_2 (0x04UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000004 */ +#define ADC_SQR3_SQ10_3 (0x08UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000008 */ +#define ADC_SQR3_SQ10_4 (0x10UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000010 */ + +#define ADC_SQR3_SQ11_Pos (6U) +#define ADC_SQR3_SQ11_Msk (0x1FUL << ADC_SQR3_SQ11_Pos) /*!< 0x000007C0 */ +#define ADC_SQR3_SQ11 ADC_SQR3_SQ11_Msk /*!< ADC 11th conversion in regular sequence */ +#define ADC_SQR3_SQ11_0 (0x01UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000040 */ +#define ADC_SQR3_SQ11_1 (0x02UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000080 */ +#define ADC_SQR3_SQ11_2 (0x04UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000100 */ +#define ADC_SQR3_SQ11_3 (0x08UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000200 */ +#define ADC_SQR3_SQ11_4 (0x10UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000400 */ + +#define ADC_SQR3_SQ12_Pos (12U) +#define ADC_SQR3_SQ12_Msk (0x1FUL << ADC_SQR3_SQ12_Pos) /*!< 0x0001F000 */ +#define ADC_SQR3_SQ12 ADC_SQR3_SQ12_Msk /*!< ADC 12th conversion in regular sequence */ +#define ADC_SQR3_SQ12_0 (0x01UL << ADC_SQR3_SQ12_Pos) /*!< 0x00001000 */ +#define ADC_SQR3_SQ12_1 (0x02UL << ADC_SQR3_SQ12_Pos) /*!< 0x00002000 */ +#define ADC_SQR3_SQ12_2 (0x04UL << ADC_SQR3_SQ12_Pos) /*!< 0x00004000 */ +#define ADC_SQR3_SQ12_3 (0x08UL << ADC_SQR3_SQ12_Pos) /*!< 0x00008000 */ +#define ADC_SQR3_SQ12_4 (0x10UL << ADC_SQR3_SQ12_Pos) /*!< 0x00010000 */ + +#define ADC_SQR3_SQ13_Pos (18U) +#define ADC_SQR3_SQ13_Msk (0x1FUL << ADC_SQR3_SQ13_Pos) /*!< 0x007C0000 */ +#define ADC_SQR3_SQ13 ADC_SQR3_SQ13_Msk /*!< ADC 13th conversion in regular sequence */ +#define ADC_SQR3_SQ13_0 (0x01UL << ADC_SQR3_SQ13_Pos) /*!< 0x00040000 */ +#define ADC_SQR3_SQ13_1 (0x02UL << ADC_SQR3_SQ13_Pos) /*!< 0x00080000 */ +#define ADC_SQR3_SQ13_2 (0x04UL << ADC_SQR3_SQ13_Pos) /*!< 0x00100000 */ +#define ADC_SQR3_SQ13_3 (0x08UL << ADC_SQR3_SQ13_Pos) /*!< 0x00200000 */ +#define ADC_SQR3_SQ13_4 (0x10UL << ADC_SQR3_SQ13_Pos) /*!< 0x00400000 */ + +#define ADC_SQR3_SQ14_Pos (24U) +#define ADC_SQR3_SQ14_Msk (0x1FUL << ADC_SQR3_SQ14_Pos) /*!< 0x1F000000 */ +#define ADC_SQR3_SQ14 ADC_SQR3_SQ14_Msk /*!< ADC 14th conversion in regular sequence */ +#define ADC_SQR3_SQ14_0 (0x01UL << ADC_SQR3_SQ14_Pos) /*!< 0x01000000 */ +#define ADC_SQR3_SQ14_1 (0x02UL << ADC_SQR3_SQ14_Pos) /*!< 0x02000000 */ +#define ADC_SQR3_SQ14_2 (0x04UL << ADC_SQR3_SQ14_Pos) /*!< 0x04000000 */ +#define ADC_SQR3_SQ14_3 (0x08UL << ADC_SQR3_SQ14_Pos) /*!< 0x08000000 */ +#define ADC_SQR3_SQ14_4 (0x10UL << ADC_SQR3_SQ14_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR4 register ********************/ +#define ADC_SQR4_SQ15_Pos (0U) +#define ADC_SQR4_SQ15_Msk (0x1FUL << ADC_SQR4_SQ15_Pos) /*!< 0x0000001F */ +#define ADC_SQR4_SQ15 ADC_SQR4_SQ15_Msk /*!< ADC 15th conversion in regular sequence */ +#define ADC_SQR4_SQ15_0 (0x01UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000001 */ +#define ADC_SQR4_SQ15_1 (0x02UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000002 */ +#define ADC_SQR4_SQ15_2 (0x04UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000004 */ +#define ADC_SQR4_SQ15_3 (0x08UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000008 */ +#define ADC_SQR4_SQ15_4 (0x10UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000010 */ + +#define ADC_SQR4_SQ16_Pos (6U) +#define ADC_SQR4_SQ16_Msk (0x1FUL << ADC_SQR4_SQ16_Pos) /*!< 0x000007C0 */ +#define ADC_SQR4_SQ16 ADC_SQR4_SQ16_Msk /*!< ADC 16th conversion in regular sequence */ +#define ADC_SQR4_SQ16_0 (0x01UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000040 */ +#define ADC_SQR4_SQ16_1 (0x02UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000080 */ +#define ADC_SQR4_SQ16_2 (0x04UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000100 */ +#define ADC_SQR4_SQ16_3 (0x08UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000200 */ +#define ADC_SQR4_SQ16_4 (0x10UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000400 */ +/******************** Bit definition for ADC_DR register ********************/ +#define ADC_DR_RDATA_Pos (0U) +#define ADC_DR_RDATA_Msk (0xFFFFFFFFUL << ADC_DR_RDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_DR_RDATA ADC_DR_RDATA_Msk /*!< ADC regular Data converted */ + +/******************** Bit definition for ADC_JSQR register ********************/ +#define ADC_JSQR_JL_Pos (0U) +#define ADC_JSQR_JL_Msk (0x3UL << ADC_JSQR_JL_Pos) /*!< 0x00000003 */ +#define ADC_JSQR_JL ADC_JSQR_JL_Msk /*!< ADC injected channel sequence length */ +#define ADC_JSQR_JL_0 (0x1UL << ADC_JSQR_JL_Pos) /*!< 0x00000001 */ +#define ADC_JSQR_JL_1 (0x2UL << ADC_JSQR_JL_Pos) /*!< 0x00000002 */ + +#define ADC_JSQR_JEXTSEL_Pos (2U) +#define ADC_JSQR_JEXTSEL_Msk (0x1FUL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x0000007C */ +#define ADC_JSQR_JEXTSEL ADC_JSQR_JEXTSEL_Msk /*!< ADC external trigger selection for injected group */ +#define ADC_JSQR_JEXTSEL_0 (0x01UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000004 */ +#define ADC_JSQR_JEXTSEL_1 (0x02UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000008 */ +#define ADC_JSQR_JEXTSEL_2 (0x04UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000010 */ +#define ADC_JSQR_JEXTSEL_3 (0x08UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_JSQR_JEXTSEL_4 (0x10UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000040 */ + +#define ADC_JSQR_JEXTEN_Pos (7U) +#define ADC_JSQR_JEXTEN_Msk (0x3UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000180 */ +#define ADC_JSQR_JEXTEN ADC_JSQR_JEXTEN_Msk /*!< ADC external trigger enable and polarity selection for injected channels */ +#define ADC_JSQR_JEXTEN_0 (0x1UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000080 */ +#define ADC_JSQR_JEXTEN_1 (0x2UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000100 */ + +#define ADC_JSQR_JSQ1_Pos (9U) +#define ADC_JSQR_JSQ1_Msk (0x1FUL << ADC_JSQR_JSQ1_Pos) /*!< 0x00003E00 */ +#define ADC_JSQR_JSQ1 ADC_JSQR_JSQ1_Msk /*!< ADC 1st conversion in injected sequence */ +#define ADC_JSQR_JSQ1_0 (0x01UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000200 */ +#define ADC_JSQR_JSQ1_1 (0x02UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000400 */ +#define ADC_JSQR_JSQ1_2 (0x04UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000800 */ +#define ADC_JSQR_JSQ1_3 (0x08UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00001000 */ +#define ADC_JSQR_JSQ1_4 (0x10UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00002000 */ + +#define ADC_JSQR_JSQ2_Pos (15U) +#define ADC_JSQR_JSQ2_Msk (0x1FUL << ADC_JSQR_JSQ2_Pos) /*!< 0x000F8000 */ +#define ADC_JSQR_JSQ2 ADC_JSQR_JSQ2_Msk /*!< ADC 2nd conversion in injected sequence */ +#define ADC_JSQR_JSQ2_0 (0x01UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00008000 */ +#define ADC_JSQR_JSQ2_1 (0x02UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00010000 */ +#define ADC_JSQR_JSQ2_2 (0x04UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00020000 */ +#define ADC_JSQR_JSQ2_3 (0x08UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00040000 */ +#define ADC_JSQR_JSQ2_4 (0x10UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00080000 */ + +#define ADC_JSQR_JSQ3_Pos (21U) +#define ADC_JSQR_JSQ3_Msk (0x1FUL << ADC_JSQR_JSQ3_Pos) /*!< 0x03E00000 */ +#define ADC_JSQR_JSQ3 ADC_JSQR_JSQ3_Msk /*!< ADC 3rd conversion in injected sequence */ +#define ADC_JSQR_JSQ3_0 (0x01UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00200000 */ +#define ADC_JSQR_JSQ3_1 (0x02UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00400000 */ +#define ADC_JSQR_JSQ3_2 (0x04UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00800000 */ +#define ADC_JSQR_JSQ3_3 (0x08UL << ADC_JSQR_JSQ3_Pos) /*!< 0x01000000 */ +#define ADC_JSQR_JSQ3_4 (0x10UL << ADC_JSQR_JSQ3_Pos) /*!< 0x02000000 */ + +#define ADC_JSQR_JSQ4_Pos (27U) +#define ADC_JSQR_JSQ4_Msk (0x1FUL << ADC_JSQR_JSQ4_Pos) /*!< 0xF8000000 */ +#define ADC_JSQR_JSQ4 ADC_JSQR_JSQ4_Msk /*!< ADC 4th conversion in injected sequence */ +#define ADC_JSQR_JSQ4_0 (0x01UL << ADC_JSQR_JSQ4_Pos) /*!< 0x08000000 */ +#define ADC_JSQR_JSQ4_1 (0x02UL << ADC_JSQR_JSQ4_Pos) /*!< 0x10000000 */ +#define ADC_JSQR_JSQ4_2 (0x04UL << ADC_JSQR_JSQ4_Pos) /*!< 0x20000000 */ +#define ADC_JSQR_JSQ4_3 (0x08UL << ADC_JSQR_JSQ4_Pos) /*!< 0x40000000 */ +#define ADC_JSQR_JSQ4_4 (0x10UL << ADC_JSQR_JSQ4_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_OFR1 register ********************/ +#define ADC_OFR1_OFFSET1_Pos (0U) +#define ADC_OFR1_OFFSET1_Msk (0x3FFFFFFUL << ADC_OFR1_OFFSET1_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR1_OFFSET1 ADC_OFR1_OFFSET1_Msk /*!< ADC data offset 1 for channel programmed into bits OFFSET1_CH[4:0] */ +#define ADC_OFR1_OFFSET1_0 (0x0000001UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000001 */ +#define ADC_OFR1_OFFSET1_1 (0x0000002UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000002 */ +#define ADC_OFR1_OFFSET1_2 (0x0000004UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000004 */ +#define ADC_OFR1_OFFSET1_3 (0x0000008UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000008 */ +#define ADC_OFR1_OFFSET1_4 (0x0000010UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000010 */ +#define ADC_OFR1_OFFSET1_5 (0x0000020UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000020 */ +#define ADC_OFR1_OFFSET1_6 (0x0000040UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000040 */ +#define ADC_OFR1_OFFSET1_7 (0x0000080UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000080 */ +#define ADC_OFR1_OFFSET1_8 (0x0000100UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000100 */ +#define ADC_OFR1_OFFSET1_9 (0x0000200UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000200 */ +#define ADC_OFR1_OFFSET1_10 (0x0000400UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000400 */ +#define ADC_OFR1_OFFSET1_11 (0x0000800UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000800 */ +#define ADC_OFR1_OFFSET1_12 (0x0001000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00001000 */ +#define ADC_OFR1_OFFSET1_13 (0x0002000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00002000 */ +#define ADC_OFR1_OFFSET1_14 (0x0004000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00004000 */ +#define ADC_OFR1_OFFSET1_15 (0x0008000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00008000 */ +#define ADC_OFR1_OFFSET1_16 (0x0010000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00010000 */ +#define ADC_OFR1_OFFSET1_17 (0x0020000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00020000 */ +#define ADC_OFR1_OFFSET1_18 (0x0040000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00040000 */ +#define ADC_OFR1_OFFSET1_19 (0x0080000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00080000 */ +#define ADC_OFR1_OFFSET1_20 (0x0100000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00100000 */ +#define ADC_OFR1_OFFSET1_21 (0x0200000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00200000 */ +#define ADC_OFR1_OFFSET1_22 (0x0400000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00400000 */ +#define ADC_OFR1_OFFSET1_23 (0x0800000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00800000 */ +#define ADC_OFR1_OFFSET1_24 (0x1000000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x01000000 */ +#define ADC_OFR1_OFFSET1_25 (0x2000000UL << ADC_OFR1_OFFSET1_Pos) /*!< 0x02000000 */ + +#define ADC_OFR1_OFFSET1_CH_Pos (26U) +#define ADC_OFR1_OFFSET1_CH_Msk (0x1FUL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR1_OFFSET1_CH ADC_OFR1_OFFSET1_CH_Msk /*!< ADC Channel selection for the data offset 1 */ +#define ADC_OFR1_OFFSET1_CH_0 (0x01UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR1_OFFSET1_CH_1 (0x02UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR1_OFFSET1_CH_2 (0x04UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR1_OFFSET1_CH_3 (0x08UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR1_OFFSET1_CH_4 (0x10UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR1_SSATE_Pos (31U) +#define ADC_OFR1_SSATE_Msk (0x1UL << ADC_OFR1_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR1_SSATE ADC_OFR1_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR1_OFFSET1_Pos (0U) +#define ADC3_OFR1_OFFSET1_Msk (0xFFFUL << ADC3_OFR1_OFFSET1_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR1_OFFSET1 ADC3_OFR1_OFFSET1_Msk /*!< ADC data offset 1 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR1_OFFSETPOS_Pos (24U) +#define ADC3_OFR1_OFFSETPOS_Msk (0x1UL << ADC3_OFR1_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR1_OFFSETPOS ADC3_OFR1_OFFSETPOS_Msk /*!< ADC offset number 1 positive */ +#define ADC3_OFR1_SATEN_Pos (25U) +#define ADC3_OFR1_SATEN_Msk (0x1UL << ADC3_OFR1_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR1_SATEN ADC3_OFR1_SATEN_Msk /*!< ADC offset number 1 saturation enable */ + +#define ADC3_OFR1_OFFSET1_EN_Pos (31U) +#define ADC3_OFR1_OFFSET1_EN_Msk (0x1UL << ADC3_OFR1_OFFSET1_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR1_OFFSET1_EN ADC3_OFR1_OFFSET1_EN_Msk /*!< ADC offset number 1 enable */ + +/******************** Bit definition for ADC_OFR2 register ********************/ +#define ADC_OFR2_OFFSET2_Pos (0U) +#define ADC_OFR2_OFFSET2_Msk (0x3FFFFFFUL << ADC_OFR2_OFFSET2_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR2_OFFSET2 ADC_OFR2_OFFSET2_Msk /*!< ADC data offset 2 for channel programmed into bits OFFSET2_CH[4:0] */ +#define ADC_OFR2_OFFSET2_0 (0x0000001UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000001 */ +#define ADC_OFR2_OFFSET2_1 (0x0000002UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000002 */ +#define ADC_OFR2_OFFSET2_2 (0x0000004UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000004 */ +#define ADC_OFR2_OFFSET2_3 (0x0000008UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000008 */ +#define ADC_OFR2_OFFSET2_4 (0x0000010UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000010 */ +#define ADC_OFR2_OFFSET2_5 (0x0000020UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000020 */ +#define ADC_OFR2_OFFSET2_6 (0x0000040UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000040 */ +#define ADC_OFR2_OFFSET2_7 (0x0000080UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000080 */ +#define ADC_OFR2_OFFSET2_8 (0x0000100UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000100 */ +#define ADC_OFR2_OFFSET2_9 (0x0000200UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000200 */ +#define ADC_OFR2_OFFSET2_10 (0x0000400UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000400 */ +#define ADC_OFR2_OFFSET2_11 (0x0000800UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000800 */ +#define ADC_OFR2_OFFSET2_12 (0x0001000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00001000 */ +#define ADC_OFR2_OFFSET2_13 (0x0002000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00002000 */ +#define ADC_OFR2_OFFSET2_14 (0x0004000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00004000 */ +#define ADC_OFR2_OFFSET2_15 (0x0008000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00008000 */ +#define ADC_OFR2_OFFSET2_16 (0x0010000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00010000 */ +#define ADC_OFR2_OFFSET2_17 (0x0020000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00020000 */ +#define ADC_OFR2_OFFSET2_18 (0x0040000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00040000 */ +#define ADC_OFR2_OFFSET2_19 (0x0080000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00080000 */ +#define ADC_OFR2_OFFSET2_20 (0x0100000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00100000 */ +#define ADC_OFR2_OFFSET2_21 (0x0200000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00200000 */ +#define ADC_OFR2_OFFSET2_22 (0x0400000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00400000 */ +#define ADC_OFR2_OFFSET2_23 (0x0800000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00800000 */ +#define ADC_OFR2_OFFSET2_24 (0x1000000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x01000000 */ +#define ADC_OFR2_OFFSET2_25 (0x2000000UL << ADC_OFR2_OFFSET2_Pos) /*!< 0x02000000 */ + +#define ADC_OFR2_OFFSET2_CH_Pos (26U) +#define ADC_OFR2_OFFSET2_CH_Msk (0x1FUL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR2_OFFSET2_CH ADC_OFR2_OFFSET2_CH_Msk /*!< ADC Channel selection for the data offset 2 */ +#define ADC_OFR2_OFFSET2_CH_0 (0x01UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR2_OFFSET2_CH_1 (0x02UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR2_OFFSET2_CH_2 (0x04UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR2_OFFSET2_CH_3 (0x08UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR2_OFFSET2_CH_4 (0x10UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR2_SSATE_Pos (31U) +#define ADC_OFR2_SSATE_Msk (0x1UL << ADC_OFR2_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR2_SSATE ADC_OFR2_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR2_OFFSET2_Pos (0U) +#define ADC3_OFR2_OFFSET2_Msk (0xFFFUL << ADC3_OFR2_OFFSET2_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR2_OFFSET2 ADC3_OFR2_OFFSET2_Msk /*!< ADC data offset 2 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR2_OFFSETPOS_Pos (24U) +#define ADC3_OFR2_OFFSETPOS_Msk (0x1UL << ADC3_OFR2_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR2_OFFSETPOS ADC3_OFR2_OFFSETPOS_Msk /*!< ADC offset number 2 positive */ +#define ADC3_OFR2_SATEN_Pos (25U) +#define ADC3_OFR2_SATEN_Msk (0x1UL << ADC3_OFR2_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR2_SATEN ADC3_OFR2_SATEN_Msk /*!< ADC offset number 2 saturation enable */ + +#define ADC3_OFR2_OFFSET2_EN_Pos (31U) +#define ADC3_OFR2_OFFSET2_EN_Msk (0x1UL << ADC3_OFR2_OFFSET2_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR2_OFFSET2_EN ADC3_OFR2_OFFSET2_EN_Msk /*!< ADC offset number 2 enable */ + +/******************** Bit definition for ADC_OFR3 register ********************/ +#define ADC_OFR3_OFFSET3_Pos (0U) +#define ADC_OFR3_OFFSET3_Msk (0x3FFFFFFUL << ADC_OFR3_OFFSET3_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR3_OFFSET3 ADC_OFR3_OFFSET3_Msk /*!< ADC data offset 3 for channel programmed into bits OFFSET3_CH[4:0] */ +#define ADC_OFR3_OFFSET3_0 (0x0000001UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000001 */ +#define ADC_OFR3_OFFSET3_1 (0x0000002UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000002 */ +#define ADC_OFR3_OFFSET3_2 (0x0000004UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000004 */ +#define ADC_OFR3_OFFSET3_3 (0x0000008UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000008 */ +#define ADC_OFR3_OFFSET3_4 (0x0000010UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000010 */ +#define ADC_OFR3_OFFSET3_5 (0x0000020UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000020 */ +#define ADC_OFR3_OFFSET3_6 (0x0000040UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000040 */ +#define ADC_OFR3_OFFSET3_7 (0x0000080UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000080 */ +#define ADC_OFR3_OFFSET3_8 (0x0000100UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000100 */ +#define ADC_OFR3_OFFSET3_9 (0x0000200UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000200 */ +#define ADC_OFR3_OFFSET3_10 (0x0000400UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000400 */ +#define ADC_OFR3_OFFSET3_11 (0x0000800UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000800 */ +#define ADC_OFR3_OFFSET3_12 (0x0001000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00001000 */ +#define ADC_OFR3_OFFSET3_13 (0x0002000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00002000 */ +#define ADC_OFR3_OFFSET3_14 (0x0004000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00004000 */ +#define ADC_OFR3_OFFSET3_15 (0x0008000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00008000 */ +#define ADC_OFR3_OFFSET3_16 (0x0010000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00010000 */ +#define ADC_OFR3_OFFSET3_17 (0x0020000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00020000 */ +#define ADC_OFR3_OFFSET3_18 (0x0040000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00040000 */ +#define ADC_OFR3_OFFSET3_19 (0x0080000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00080000 */ +#define ADC_OFR3_OFFSET3_20 (0x0100000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00100000 */ +#define ADC_OFR3_OFFSET3_21 (0x0200000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00200000 */ +#define ADC_OFR3_OFFSET3_22 (0x0400000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00400000 */ +#define ADC_OFR3_OFFSET3_23 (0x0800000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00800000 */ +#define ADC_OFR3_OFFSET3_24 (0x1000000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x01000000 */ +#define ADC_OFR3_OFFSET3_25 (0x2000000UL << ADC_OFR3_OFFSET3_Pos) /*!< 0x02000000 */ + +#define ADC_OFR3_OFFSET3_CH_Pos (26U) +#define ADC_OFR3_OFFSET3_CH_Msk (0x1FUL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR3_OFFSET3_CH ADC_OFR3_OFFSET3_CH_Msk /*!< ADC Channel selection for the data offset 3 */ +#define ADC_OFR3_OFFSET3_CH_0 (0x01UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR3_OFFSET3_CH_1 (0x02UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR3_OFFSET3_CH_2 (0x04UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR3_OFFSET3_CH_3 (0x08UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR3_OFFSET3_CH_4 (0x10UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR3_SSATE_Pos (31U) +#define ADC_OFR3_SSATE_Msk (0x1UL << ADC_OFR3_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR3_SSATE ADC_OFR3_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR3_OFFSET3_Pos (0U) +#define ADC3_OFR3_OFFSET3_Msk (0xFFFUL << ADC3_OFR3_OFFSET3_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR3_OFFSET3 ADC3_OFR3_OFFSET3_Msk /*!< ADC data offset 3 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR3_OFFSETPOS_Pos (24U) +#define ADC3_OFR3_OFFSETPOS_Msk (0x1UL << ADC3_OFR3_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR3_OFFSETPOS ADC3_OFR3_OFFSETPOS_Msk /*!< ADC offset number 3 positive */ +#define ADC3_OFR3_SATEN_Pos (25U) +#define ADC3_OFR3_SATEN_Msk (0x1UL << ADC3_OFR3_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR3_SATEN ADC3_OFR3_SATEN_Msk /*!< ADC offset number 3 saturation enable */ + +#define ADC3_OFR3_OFFSET3_EN_Pos (31U) +#define ADC3_OFR3_OFFSET3_EN_Msk (0x1UL << ADC3_OFR3_OFFSET3_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR3_OFFSET3_EN ADC3_OFR3_OFFSET3_EN_Msk /*!< ADC offset number 3 enable */ + +/******************** Bit definition for ADC_OFR4 register ********************/ +#define ADC_OFR4_OFFSET4_Pos (0U) +#define ADC_OFR4_OFFSET4_Msk (0x3FFFFFFUL << ADC_OFR4_OFFSET4_Pos) /*!< 0x03FFFFFF */ +#define ADC_OFR4_OFFSET4 ADC_OFR4_OFFSET4_Msk /*!< ADC data offset 4 for channel programmed into bits OFFSET4_CH[4:0] */ +#define ADC_OFR4_OFFSET4_0 (0x0000001UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000001 */ +#define ADC_OFR4_OFFSET4_1 (0x0000002UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000002 */ +#define ADC_OFR4_OFFSET4_2 (0x0000004UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000004 */ +#define ADC_OFR4_OFFSET4_3 (0x0000008UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000008 */ +#define ADC_OFR4_OFFSET4_4 (0x0000010UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000010 */ +#define ADC_OFR4_OFFSET4_5 (0x0000020UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000020 */ +#define ADC_OFR4_OFFSET4_6 (0x0000040UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000040 */ +#define ADC_OFR4_OFFSET4_7 (0x0000080UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000080 */ +#define ADC_OFR4_OFFSET4_8 (0x0000100UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000100 */ +#define ADC_OFR4_OFFSET4_9 (0x0000200UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000200 */ +#define ADC_OFR4_OFFSET4_10 (0x0000400UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000400 */ +#define ADC_OFR4_OFFSET4_11 (0x0000800UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000800 */ +#define ADC_OFR4_OFFSET4_12 (0x0001000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00001000 */ +#define ADC_OFR4_OFFSET4_13 (0x0002000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00002000 */ +#define ADC_OFR4_OFFSET4_14 (0x0004000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00004000 */ +#define ADC_OFR4_OFFSET4_15 (0x0008000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00008000 */ +#define ADC_OFR4_OFFSET4_16 (0x0010000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00010000 */ +#define ADC_OFR4_OFFSET4_17 (0x0020000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00020000 */ +#define ADC_OFR4_OFFSET4_18 (0x0040000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00040000 */ +#define ADC_OFR4_OFFSET4_19 (0x0080000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00080000 */ +#define ADC_OFR4_OFFSET4_20 (0x0100000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00100000 */ +#define ADC_OFR4_OFFSET4_21 (0x0200000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00200000 */ +#define ADC_OFR4_OFFSET4_22 (0x0400000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00400000 */ +#define ADC_OFR4_OFFSET4_23 (0x0800000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00800000 */ +#define ADC_OFR4_OFFSET4_24 (0x1000000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x01000000 */ +#define ADC_OFR4_OFFSET4_25 (0x2000000UL << ADC_OFR4_OFFSET4_Pos) /*!< 0x02000000 */ + +#define ADC_OFR4_OFFSET4_CH_Pos (26U) +#define ADC_OFR4_OFFSET4_CH_Msk (0x1FUL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR4_OFFSET4_CH ADC_OFR4_OFFSET4_CH_Msk /*!< ADC Channel selection for the data offset 4 */ +#define ADC_OFR4_OFFSET4_CH_0 (0x01UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR4_OFFSET4_CH_1 (0x02UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR4_OFFSET4_CH_2 (0x04UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR4_OFFSET4_CH_3 (0x08UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR4_OFFSET4_CH_4 (0x10UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR4_SSATE_Pos (31U) +#define ADC_OFR4_SSATE_Msk (0x1UL << ADC_OFR4_SSATE_Pos) /*!< 0x80000000 */ +#define ADC_OFR4_SSATE ADC_OFR4_SSATE_Msk /*!< ADC Signed saturation Enable */ + +#define ADC3_OFR4_OFFSET4_Pos (0U) +#define ADC3_OFR4_OFFSET4_Msk (0xFFFUL << ADC3_OFR4_OFFSET4_Pos) /*!< 0x00000FFF */ +#define ADC3_OFR4_OFFSET4 ADC3_OFR4_OFFSET4_Msk /*!< ADC data offset 4 for channel programmed into bits OFFSET1_CH[4:0] */ + +#define ADC3_OFR4_OFFSETPOS_Pos (24U) +#define ADC3_OFR4_OFFSETPOS_Msk (0x1UL << ADC3_OFR4_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC3_OFR4_OFFSETPOS ADC3_OFR4_OFFSETPOS_Msk /*!< ADC offset number 4 positive */ +#define ADC3_OFR4_SATEN_Pos (25U) +#define ADC3_OFR4_SATEN_Msk (0x1UL << ADC3_OFR4_SATEN_Pos) /*!< 0x02000000 */ +#define ADC3_OFR4_SATEN ADC3_OFR4_SATEN_Msk /*!< ADC offset number 4 saturation enable */ + +#define ADC3_OFR4_OFFSET4_EN_Pos (31U) +#define ADC3_OFR4_OFFSET4_EN_Msk (0x1UL << ADC3_OFR4_OFFSET4_EN_Pos) /*!< 0x80000000 */ +#define ADC3_OFR4_OFFSET4_EN ADC3_OFR4_OFFSET4_EN_Msk /*!< ADC offset number 4 enable */ + +/******************** Bit definition for ADC_JDR1 register ********************/ +#define ADC_JDR1_JDATA_Pos (0U) +#define ADC_JDR1_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR1_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR1_JDATA ADC_JDR1_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR1_JDATA_0 (0x00000001UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR1_JDATA_1 (0x00000002UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR1_JDATA_2 (0x00000004UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR1_JDATA_3 (0x00000008UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR1_JDATA_4 (0x00000010UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR1_JDATA_5 (0x00000020UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR1_JDATA_6 (0x00000040UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR1_JDATA_7 (0x00000080UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR1_JDATA_8 (0x00000100UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR1_JDATA_9 (0x00000200UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR1_JDATA_10 (0x00000400UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR1_JDATA_11 (0x00000800UL << ADC_JDR1_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR1_JDATA_12 (0x00001000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR1_JDATA_13 (0x00002000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR1_JDATA_14 (0x00004000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR1_JDATA_15 (0x00008000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR1_JDATA_16 (0x00010000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR1_JDATA_17 (0x00020000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR1_JDATA_18 (0x00040000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR1_JDATA_19 (0x00080000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR1_JDATA_20 (0x00100000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR1_JDATA_21 (0x00200000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR1_JDATA_22 (0x00400000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR1_JDATA_23 (0x00800000UL << ADC_JDR1_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR1_JDATA_24 (0x01000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR1_JDATA_25 (0x02000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR1_JDATA_26 (0x04000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR1_JDATA_27 (0x08000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR1_JDATA_28 (0x10000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR1_JDATA_29 (0x20000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR1_JDATA_30 (0x40000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR1_JDATA_31 (0x80000000UL << ADC_JDR1_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR2 register ********************/ +#define ADC_JDR2_JDATA_Pos (0U) +#define ADC_JDR2_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR2_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR2_JDATA ADC_JDR2_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR2_JDATA_0 (0x00000001UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR2_JDATA_1 (0x00000002UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR2_JDATA_2 (0x00000004UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR2_JDATA_3 (0x00000008UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR2_JDATA_4 (0x00000010UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR2_JDATA_5 (0x00000020UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR2_JDATA_6 (0x00000040UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR2_JDATA_7 (0x00000080UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR2_JDATA_8 (0x00000100UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR2_JDATA_9 (0x00000200UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR2_JDATA_10 (0x00000400UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR2_JDATA_11 (0x00000800UL << ADC_JDR2_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR2_JDATA_12 (0x00001000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR2_JDATA_13 (0x00002000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR2_JDATA_14 (0x00004000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR2_JDATA_15 (0x00008000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR2_JDATA_16 (0x00010000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR2_JDATA_17 (0x00020000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR2_JDATA_18 (0x00040000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR2_JDATA_19 (0x00080000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR2_JDATA_20 (0x00100000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR2_JDATA_21 (0x00200000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR2_JDATA_22 (0x00400000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR2_JDATA_23 (0x00800000UL << ADC_JDR2_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR2_JDATA_24 (0x01000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR2_JDATA_25 (0x02000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR2_JDATA_26 (0x04000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR2_JDATA_27 (0x08000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR2_JDATA_28 (0x10000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR2_JDATA_29 (0x20000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR2_JDATA_30 (0x40000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR2_JDATA_31 (0x80000000UL << ADC_JDR2_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR3 register ********************/ +#define ADC_JDR3_JDATA_Pos (0U) +#define ADC_JDR3_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR3_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR3_JDATA ADC_JDR3_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR3_JDATA_0 (0x00000001UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR3_JDATA_1 (0x00000002UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR3_JDATA_2 (0x00000004UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR3_JDATA_3 (0x00000008UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR3_JDATA_4 (0x00000010UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR3_JDATA_5 (0x00000020UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR3_JDATA_6 (0x00000040UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR3_JDATA_7 (0x00000080UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR3_JDATA_8 (0x00000100UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR3_JDATA_9 (0x00000200UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR3_JDATA_10 (0x00000400UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR3_JDATA_11 (0x00000800UL << ADC_JDR3_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR3_JDATA_12 (0x00001000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR3_JDATA_13 (0x00002000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR3_JDATA_14 (0x00004000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR3_JDATA_15 (0x00008000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR3_JDATA_16 (0x00010000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR3_JDATA_17 (0x00020000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR3_JDATA_18 (0x00040000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR3_JDATA_19 (0x00080000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR3_JDATA_20 (0x00100000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR3_JDATA_21 (0x00200000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR3_JDATA_22 (0x00400000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR3_JDATA_23 (0x00800000UL << ADC_JDR3_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR3_JDATA_24 (0x01000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR3_JDATA_25 (0x02000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR3_JDATA_26 (0x04000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR3_JDATA_27 (0x08000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR3_JDATA_28 (0x10000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR3_JDATA_29 (0x20000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR3_JDATA_30 (0x40000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR3_JDATA_31 (0x80000000UL << ADC_JDR3_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_JDR4 register ********************/ +#define ADC_JDR4_JDATA_Pos (0U) +#define ADC_JDR4_JDATA_Msk (0xFFFFFFFFUL << ADC_JDR4_JDATA_Pos) /*!< 0xFFFFFFFF */ +#define ADC_JDR4_JDATA ADC_JDR4_JDATA_Msk /*!< ADC Injected DATA */ +#define ADC_JDR4_JDATA_0 (0x00000001UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000001 */ +#define ADC_JDR4_JDATA_1 (0x00000002UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000002 */ +#define ADC_JDR4_JDATA_2 (0x00000004UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000004 */ +#define ADC_JDR4_JDATA_3 (0x00000008UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000008 */ +#define ADC_JDR4_JDATA_4 (0x00000010UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000010 */ +#define ADC_JDR4_JDATA_5 (0x00000020UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000020 */ +#define ADC_JDR4_JDATA_6 (0x00000040UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000040 */ +#define ADC_JDR4_JDATA_7 (0x00000080UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000080 */ +#define ADC_JDR4_JDATA_8 (0x00000100UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000100 */ +#define ADC_JDR4_JDATA_9 (0x00000200UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000200 */ +#define ADC_JDR4_JDATA_10 (0x00000400UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000400 */ +#define ADC_JDR4_JDATA_11 (0x00000800UL << ADC_JDR4_JDATA_Pos) /*!< 0x00000800 */ +#define ADC_JDR4_JDATA_12 (0x00001000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00001000 */ +#define ADC_JDR4_JDATA_13 (0x00002000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00002000 */ +#define ADC_JDR4_JDATA_14 (0x00004000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00004000 */ +#define ADC_JDR4_JDATA_15 (0x00008000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00008000 */ +#define ADC_JDR4_JDATA_16 (0x00010000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00010000 */ +#define ADC_JDR4_JDATA_17 (0x00020000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00020000 */ +#define ADC_JDR4_JDATA_18 (0x00040000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00040000 */ +#define ADC_JDR4_JDATA_19 (0x00080000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00080000 */ +#define ADC_JDR4_JDATA_20 (0x00100000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00100000 */ +#define ADC_JDR4_JDATA_21 (0x00200000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00200000 */ +#define ADC_JDR4_JDATA_22 (0x00400000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00400000 */ +#define ADC_JDR4_JDATA_23 (0x00800000UL << ADC_JDR4_JDATA_Pos) /*!< 0x00800000 */ +#define ADC_JDR4_JDATA_24 (0x01000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x01000000 */ +#define ADC_JDR4_JDATA_25 (0x02000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x02000000 */ +#define ADC_JDR4_JDATA_26 (0x04000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x04000000 */ +#define ADC_JDR4_JDATA_27 (0x08000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x08000000 */ +#define ADC_JDR4_JDATA_28 (0x10000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x10000000 */ +#define ADC_JDR4_JDATA_29 (0x20000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x20000000 */ +#define ADC_JDR4_JDATA_30 (0x40000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x40000000 */ +#define ADC_JDR4_JDATA_31 (0x80000000UL << ADC_JDR4_JDATA_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_AWD2CR register ********************/ +#define ADC_AWD2CR_AWD2CH_Pos (0U) +#define ADC_AWD2CR_AWD2CH_Msk (0xFFFFFUL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x000FFFFF */ +#define ADC_AWD2CR_AWD2CH ADC_AWD2CR_AWD2CH_Msk /*!< ADC Analog watchdog 2 channel selection */ +#define ADC_AWD2CR_AWD2CH_0 (0x00001UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD2CR_AWD2CH_1 (0x00002UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD2CR_AWD2CH_2 (0x00004UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD2CR_AWD2CH_3 (0x00008UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD2CR_AWD2CH_4 (0x00010UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD2CR_AWD2CH_5 (0x00020UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD2CR_AWD2CH_6 (0x00040UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD2CR_AWD2CH_7 (0x00080UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD2CR_AWD2CH_8 (0x00100UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD2CR_AWD2CH_9 (0x00200UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD2CR_AWD2CH_10 (0x00400UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD2CR_AWD2CH_11 (0x00800UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD2CR_AWD2CH_12 (0x01000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD2CR_AWD2CH_13 (0x02000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD2CR_AWD2CH_14 (0x04000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD2CR_AWD2CH_15 (0x08000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD2CR_AWD2CH_16 (0x10000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD2CR_AWD2CH_17 (0x20000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD2CR_AWD2CH_18 (0x40000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00040000 */ +#define ADC_AWD2CR_AWD2CH_19 (0x80000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_AWD3CR register ********************/ +#define ADC_AWD3CR_AWD3CH_Pos (0U) +#define ADC_AWD3CR_AWD3CH_Msk (0xFFFFFUL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x000FFFFF */ +#define ADC_AWD3CR_AWD3CH ADC_AWD3CR_AWD3CH_Msk /*!< ADC Analog watchdog 2 channel selection */ +#define ADC_AWD3CR_AWD3CH_0 (0x00001UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD3CR_AWD3CH_1 (0x00002UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD3CR_AWD3CH_2 (0x00004UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD3CR_AWD3CH_3 (0x00008UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD3CR_AWD3CH_4 (0x00010UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD3CR_AWD3CH_5 (0x00020UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD3CR_AWD3CH_6 (0x00040UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD3CR_AWD3CH_7 (0x00080UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD3CR_AWD3CH_8 (0x00100UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD3CR_AWD3CH_9 (0x00200UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD3CR_AWD3CH_10 (0x00400UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD3CR_AWD3CH_11 (0x00800UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD3CR_AWD3CH_12 (0x01000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD3CR_AWD3CH_13 (0x02000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD3CR_AWD3CH_14 (0x04000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD3CR_AWD3CH_15 (0x08000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD3CR_AWD3CH_16 (0x10000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD3CR_AWD3CH_17 (0x20000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD3CR_AWD3CH_18 (0x40000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00040000 */ +#define ADC_AWD3CR_AWD3CH_19 (0x80000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_DIFSEL register ********************/ +#define ADC_DIFSEL_DIFSEL_Pos (0U) +#define ADC_DIFSEL_DIFSEL_Msk (0xFFFFFUL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x000FFFFF */ +#define ADC_DIFSEL_DIFSEL ADC_DIFSEL_DIFSEL_Msk /*!< ADC differential modes for channels 1 to 18 */ +#define ADC_DIFSEL_DIFSEL_0 (0x00001UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000001 */ +#define ADC_DIFSEL_DIFSEL_1 (0x00002UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000002 */ +#define ADC_DIFSEL_DIFSEL_2 (0x00004UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000004 */ +#define ADC_DIFSEL_DIFSEL_3 (0x00008UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000008 */ +#define ADC_DIFSEL_DIFSEL_4 (0x00010UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000010 */ +#define ADC_DIFSEL_DIFSEL_5 (0x00020UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000020 */ +#define ADC_DIFSEL_DIFSEL_6 (0x00040UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000040 */ +#define ADC_DIFSEL_DIFSEL_7 (0x00080UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000080 */ +#define ADC_DIFSEL_DIFSEL_8 (0x00100UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000100 */ +#define ADC_DIFSEL_DIFSEL_9 (0x00200UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000200 */ +#define ADC_DIFSEL_DIFSEL_10 (0x00400UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000400 */ +#define ADC_DIFSEL_DIFSEL_11 (0x00800UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000800 */ +#define ADC_DIFSEL_DIFSEL_12 (0x01000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00001000 */ +#define ADC_DIFSEL_DIFSEL_13 (0x02000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00002000 */ +#define ADC_DIFSEL_DIFSEL_14 (0x04000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00004000 */ +#define ADC_DIFSEL_DIFSEL_15 (0x08000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00008000 */ +#define ADC_DIFSEL_DIFSEL_16 (0x10000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00010000 */ +#define ADC_DIFSEL_DIFSEL_17 (0x20000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00020000 */ +#define ADC_DIFSEL_DIFSEL_18 (0x40000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00040000 */ +#define ADC_DIFSEL_DIFSEL_19 (0x80000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00080000 */ + +/******************** Bit definition for ADC_CALFACT register ********************/ +#define ADC_CALFACT_CALFACT_S_Pos (0U) +#define ADC_CALFACT_CALFACT_S_Msk (0x7FFUL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x000007FF */ +#define ADC_CALFACT_CALFACT_S ADC_CALFACT_CALFACT_S_Msk /*!< ADC calibration factors in single-ended mode */ +#define ADC_CALFACT_CALFACT_S_0 (0x001UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT_CALFACT_S_1 (0x002UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT_CALFACT_S_2 (0x004UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT_CALFACT_S_3 (0x008UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT_CALFACT_S_4 (0x010UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT_CALFACT_S_5 (0x020UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT_CALFACT_S_6 (0x040UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000040 */ +#define ADC_CALFACT_CALFACT_S_7 (0x080UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000080 */ +#define ADC_CALFACT_CALFACT_S_8 (0x100UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000100 */ +#define ADC_CALFACT_CALFACT_S_9 (0x200UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000200 */ +#define ADC_CALFACT_CALFACT_S_10 (0x400UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000400 */ +#define ADC_CALFACT_CALFACT_D_Pos (16U) +#define ADC_CALFACT_CALFACT_D_Msk (0x7FFUL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x07FF0000 */ +#define ADC_CALFACT_CALFACT_D ADC_CALFACT_CALFACT_D_Msk /*!< ADC calibration factors in differential mode */ +#define ADC_CALFACT_CALFACT_D_0 (0x001UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT_CALFACT_D_1 (0x002UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT_CALFACT_D_2 (0x004UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT_CALFACT_D_3 (0x008UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT_CALFACT_D_4 (0x010UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT_CALFACT_D_5 (0x020UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT_CALFACT_D_6 (0x040UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00400000 */ +#define ADC_CALFACT_CALFACT_D_7 (0x080UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00800000 */ +#define ADC_CALFACT_CALFACT_D_8 (0x100UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x01000000 */ +#define ADC_CALFACT_CALFACT_D_9 (0x200UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x02000000 */ +#define ADC_CALFACT_CALFACT_D_10 (0x400UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x04000000 */ + +/******************** Bit definition for ADC_CALFACT2 register ********************/ +#define ADC_CALFACT2_LINCALFACT_Pos (0U) +#define ADC_CALFACT2_LINCALFACT_Msk (0x3FFFFFFFUL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x3FFFFFFF */ +#define ADC_CALFACT2_LINCALFACT ADC_CALFACT2_LINCALFACT_Msk /*!< ADC Linearity calibration factors */ +#define ADC_CALFACT2_LINCALFACT_0 (0x00000001UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT2_LINCALFACT_1 (0x00000002UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT2_LINCALFACT_2 (0x00000004UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT2_LINCALFACT_3 (0x00000008UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT2_LINCALFACT_4 (0x00000010UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT2_LINCALFACT_5 (0x00000020UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT2_LINCALFACT_6 (0x00000040UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000040 */ +#define ADC_CALFACT2_LINCALFACT_7 (0x00000080UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000080 */ +#define ADC_CALFACT2_LINCALFACT_8 (0x00000100UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000100 */ +#define ADC_CALFACT2_LINCALFACT_9 (0x00000200UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000200 */ +#define ADC_CALFACT2_LINCALFACT_10 (0x00000400UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000400 */ +#define ADC_CALFACT2_LINCALFACT_11 (0x00000800UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00000800 */ +#define ADC_CALFACT2_LINCALFACT_12 (0x00001000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00001000 */ +#define ADC_CALFACT2_LINCALFACT_13 (0x00002000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00002000 */ +#define ADC_CALFACT2_LINCALFACT_14 (0x00004000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00004000 */ +#define ADC_CALFACT2_LINCALFACT_15 (0x00008000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00008000 */ +#define ADC_CALFACT2_LINCALFACT_16 (0x00010000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT2_LINCALFACT_17 (0x00020000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT2_LINCALFACT_18 (0x00040000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT2_LINCALFACT_19 (0x00080000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT2_LINCALFACT_20 (0x00100000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT2_LINCALFACT_21 (0x00200000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT2_LINCALFACT_22 (0x00400000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00400000 */ +#define ADC_CALFACT2_LINCALFACT_23 (0x00800000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x00800000 */ +#define ADC_CALFACT2_LINCALFACT_24 (0x01000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x01000000 */ +#define ADC_CALFACT2_LINCALFACT_25 (0x02000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x02000000 */ +#define ADC_CALFACT2_LINCALFACT_26 (0x04000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x04000000 */ +#define ADC_CALFACT2_LINCALFACT_27 (0x08000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x08000000 */ +#define ADC_CALFACT2_LINCALFACT_28 (0x10000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x10000000 */ +#define ADC_CALFACT2_LINCALFACT_29 (0x20000000UL << ADC_CALFACT2_LINCALFACT_Pos) /*!< 0x20000000 */ + +/************************* ADC Common registers *****************************/ +/******************** Bit definition for ADC_CSR register ********************/ +#define ADC_CSR_ADRDY_MST_Pos (0U) +#define ADC_CSR_ADRDY_MST_Msk (0x1UL << ADC_CSR_ADRDY_MST_Pos) /*!< 0x00000001 */ +#define ADC_CSR_ADRDY_MST ADC_CSR_ADRDY_MST_Msk /*!< Master ADC ready */ +#define ADC_CSR_EOSMP_MST_Pos (1U) +#define ADC_CSR_EOSMP_MST_Msk (0x1UL << ADC_CSR_EOSMP_MST_Pos) /*!< 0x00000002 */ +#define ADC_CSR_EOSMP_MST ADC_CSR_EOSMP_MST_Msk /*!< End of sampling phase flag of the master ADC */ +#define ADC_CSR_EOC_MST_Pos (2U) +#define ADC_CSR_EOC_MST_Msk (0x1UL << ADC_CSR_EOC_MST_Pos) /*!< 0x00000004 */ +#define ADC_CSR_EOC_MST ADC_CSR_EOC_MST_Msk /*!< End of regular conversion of the master ADC */ +#define ADC_CSR_EOS_MST_Pos (3U) +#define ADC_CSR_EOS_MST_Msk (0x1UL << ADC_CSR_EOS_MST_Pos) /*!< 0x00000008 */ +#define ADC_CSR_EOS_MST ADC_CSR_EOS_MST_Msk /*!< End of regular sequence flag of the master ADC */ +#define ADC_CSR_OVR_MST_Pos (4U) +#define ADC_CSR_OVR_MST_Msk (0x1UL << ADC_CSR_OVR_MST_Pos) /*!< 0x00000010 */ +#define ADC_CSR_OVR_MST ADC_CSR_OVR_MST_Msk /*!< Overrun flag of the master ADC */ +#define ADC_CSR_JEOC_MST_Pos (5U) +#define ADC_CSR_JEOC_MST_Msk (0x1UL << ADC_CSR_JEOC_MST_Pos) /*!< 0x00000020 */ +#define ADC_CSR_JEOC_MST ADC_CSR_JEOC_MST_Msk /*!< End of injected conversion of the master ADC */ +#define ADC_CSR_JEOS_MST_Pos (6U) +#define ADC_CSR_JEOS_MST_Msk (0x1UL << ADC_CSR_JEOS_MST_Pos) /*!< 0x00000040 */ +#define ADC_CSR_JEOS_MST ADC_CSR_JEOS_MST_Msk /*!< End of injected sequence flag of the master ADC */ +#define ADC_CSR_AWD1_MST_Pos (7U) +#define ADC_CSR_AWD1_MST_Msk (0x1UL << ADC_CSR_AWD1_MST_Pos) /*!< 0x00000080 */ +#define ADC_CSR_AWD1_MST ADC_CSR_AWD1_MST_Msk /*!< Analog watchdog 1 flag of the master ADC */ +#define ADC_CSR_AWD2_MST_Pos (8U) +#define ADC_CSR_AWD2_MST_Msk (0x1UL << ADC_CSR_AWD2_MST_Pos) /*!< 0x00000100 */ +#define ADC_CSR_AWD2_MST ADC_CSR_AWD2_MST_Msk /*!< Analog watchdog 2 flag of the master ADC */ +#define ADC_CSR_AWD3_MST_Pos (9U) +#define ADC_CSR_AWD3_MST_Msk (0x1UL << ADC_CSR_AWD3_MST_Pos) /*!< 0x00000200 */ +#define ADC_CSR_AWD3_MST ADC_CSR_AWD3_MST_Msk /*!< Analog watchdog 3 flag of the master ADC */ +#define ADC_CSR_JQOVF_MST_Pos (10U) +#define ADC_CSR_JQOVF_MST_Msk (0x1UL << ADC_CSR_JQOVF_MST_Pos) /*!< 0x00000400 */ +#define ADC_CSR_JQOVF_MST ADC_CSR_JQOVF_MST_Msk /*!< Injected context queue overflow flag of the master ADC */ +#define ADC_CSR_ADRDY_SLV_Pos (16U) +#define ADC_CSR_ADRDY_SLV_Msk (0x1UL << ADC_CSR_ADRDY_SLV_Pos) /*!< 0x00010000 */ +#define ADC_CSR_ADRDY_SLV ADC_CSR_ADRDY_SLV_Msk /*!< Slave ADC ready */ +#define ADC_CSR_EOSMP_SLV_Pos (17U) +#define ADC_CSR_EOSMP_SLV_Msk (0x1UL << ADC_CSR_EOSMP_SLV_Pos) /*!< 0x00020000 */ +#define ADC_CSR_EOSMP_SLV ADC_CSR_EOSMP_SLV_Msk /*!< End of sampling phase flag of the slave ADC */ +#define ADC_CSR_EOC_SLV_Pos (18U) +#define ADC_CSR_EOC_SLV_Msk (0x1UL << ADC_CSR_EOC_SLV_Pos) /*!< 0x00040000 */ +#define ADC_CSR_EOC_SLV ADC_CSR_EOC_SLV_Msk /*!< End of regular conversion of the slave ADC */ +#define ADC_CSR_EOS_SLV_Pos (19U) +#define ADC_CSR_EOS_SLV_Msk (0x1UL << ADC_CSR_EOS_SLV_Pos) /*!< 0x00080000 */ +#define ADC_CSR_EOS_SLV ADC_CSR_EOS_SLV_Msk /*!< End of regular sequence flag of the slave ADC */ +#define ADC_CSR_OVR_SLV_Pos (20U) +#define ADC_CSR_OVR_SLV_Msk (0x1UL << ADC_CSR_OVR_SLV_Pos) /*!< 0x00100000 */ +#define ADC_CSR_OVR_SLV ADC_CSR_OVR_SLV_Msk /*!< Overrun flag of the slave ADC */ +#define ADC_CSR_JEOC_SLV_Pos (21U) +#define ADC_CSR_JEOC_SLV_Msk (0x1UL << ADC_CSR_JEOC_SLV_Pos) /*!< 0x00200000 */ +#define ADC_CSR_JEOC_SLV ADC_CSR_JEOC_SLV_Msk /*!< End of injected conversion of the slave ADC */ +#define ADC_CSR_JEOS_SLV_Pos (22U) +#define ADC_CSR_JEOS_SLV_Msk (0x1UL << ADC_CSR_JEOS_SLV_Pos) /*!< 0x00400000 */ +#define ADC_CSR_JEOS_SLV ADC_CSR_JEOS_SLV_Msk /*!< End of injected sequence flag of the slave ADC */ +#define ADC_CSR_AWD1_SLV_Pos (23U) +#define ADC_CSR_AWD1_SLV_Msk (0x1UL << ADC_CSR_AWD1_SLV_Pos) /*!< 0x00800000 */ +#define ADC_CSR_AWD1_SLV ADC_CSR_AWD1_SLV_Msk /*!< Analog watchdog 1 flag of the slave ADC */ +#define ADC_CSR_AWD2_SLV_Pos (24U) +#define ADC_CSR_AWD2_SLV_Msk (0x1UL << ADC_CSR_AWD2_SLV_Pos) /*!< 0x01000000 */ +#define ADC_CSR_AWD2_SLV ADC_CSR_AWD2_SLV_Msk /*!< Analog watchdog 2 flag of the slave ADC */ +#define ADC_CSR_AWD3_SLV_Pos (25U) +#define ADC_CSR_AWD3_SLV_Msk (0x1UL << ADC_CSR_AWD3_SLV_Pos) /*!< 0x02000000 */ +#define ADC_CSR_AWD3_SLV ADC_CSR_AWD3_SLV_Msk /*!< Analog watchdog 3 flag of the slave ADC */ +#define ADC_CSR_JQOVF_SLV_Pos (26U) +#define ADC_CSR_JQOVF_SLV_Msk (0x1UL << ADC_CSR_JQOVF_SLV_Pos) /*!< 0x04000000 */ +#define ADC_CSR_JQOVF_SLV ADC_CSR_JQOVF_SLV_Msk /*!< Injected context queue overflow flag of the slave ADC */ + +/******************** Bit definition for ADC_CCR register ********************/ +#define ADC_CCR_DUAL_Pos (0U) +#define ADC_CCR_DUAL_Msk (0x1FUL << ADC_CCR_DUAL_Pos) /*!< 0x0000001F */ +#define ADC_CCR_DUAL ADC_CCR_DUAL_Msk /*!< Dual ADC mode selection */ +#define ADC_CCR_DUAL_0 (0x01UL << ADC_CCR_DUAL_Pos) /*!< 0x00000001 */ +#define ADC_CCR_DUAL_1 (0x02UL << ADC_CCR_DUAL_Pos) /*!< 0x00000002 */ +#define ADC_CCR_DUAL_2 (0x04UL << ADC_CCR_DUAL_Pos) /*!< 0x00000004 */ +#define ADC_CCR_DUAL_3 (0x08UL << ADC_CCR_DUAL_Pos) /*!< 0x00000008 */ +#define ADC_CCR_DUAL_4 (0x10UL << ADC_CCR_DUAL_Pos) /*!< 0x00000010 */ + +#define ADC_CCR_DELAY_Pos (8U) +#define ADC_CCR_DELAY_Msk (0xFUL << ADC_CCR_DELAY_Pos) /*!< 0x00000F00 */ +#define ADC_CCR_DELAY ADC_CCR_DELAY_Msk /*!< Delay between 2 sampling phases */ +#define ADC_CCR_DELAY_0 (0x1UL << ADC_CCR_DELAY_Pos) /*!< 0x00000100 */ +#define ADC_CCR_DELAY_1 (0x2UL << ADC_CCR_DELAY_Pos) /*!< 0x00000200 */ +#define ADC_CCR_DELAY_2 (0x4UL << ADC_CCR_DELAY_Pos) /*!< 0x00000400 */ +#define ADC_CCR_DELAY_3 (0x8UL << ADC_CCR_DELAY_Pos) /*!< 0x00000800 */ + + +#define ADC_CCR_DAMDF_Pos (14U) +#define ADC_CCR_DAMDF_Msk (0x3UL << ADC_CCR_DAMDF_Pos) /*!< 0x0000C000 */ +#define ADC_CCR_DAMDF ADC_CCR_DAMDF_Msk /*!< Dual ADC mode Data format */ +#define ADC_CCR_DAMDF_0 (0x1UL << ADC_CCR_DAMDF_Pos) /*!< 0x00004000 */ +#define ADC_CCR_DAMDF_1 (0x2UL << ADC_CCR_DAMDF_Pos) /*!< 0x00008000 */ + +#define ADC_CCR_CKMODE_Pos (16U) +#define ADC_CCR_CKMODE_Msk (0x3UL << ADC_CCR_CKMODE_Pos) /*!< 0x00030000 */ +#define ADC_CCR_CKMODE ADC_CCR_CKMODE_Msk /*!< ADC clock mode */ +#define ADC_CCR_CKMODE_0 (0x1UL << ADC_CCR_CKMODE_Pos) /*!< 0x00010000 */ +#define ADC_CCR_CKMODE_1 (0x2UL << ADC_CCR_CKMODE_Pos) /*!< 0x00020000 */ + +#define ADC_CCR_PRESC_Pos (18U) +#define ADC_CCR_PRESC_Msk (0xFUL << ADC_CCR_PRESC_Pos) /*!< 0x003C0000 */ +#define ADC_CCR_PRESC ADC_CCR_PRESC_Msk /*!< ADC prescaler */ +#define ADC_CCR_PRESC_0 (0x1UL << ADC_CCR_PRESC_Pos) /*!< 0x00040000 */ +#define ADC_CCR_PRESC_1 (0x2UL << ADC_CCR_PRESC_Pos) /*!< 0x00080000 */ +#define ADC_CCR_PRESC_2 (0x4UL << ADC_CCR_PRESC_Pos) /*!< 0x00100000 */ +#define ADC_CCR_PRESC_3 (0x8UL << ADC_CCR_PRESC_Pos) /*!< 0x00200000 */ + +#define ADC_CCR_VREFEN_Pos (22U) +#define ADC_CCR_VREFEN_Msk (0x1UL << ADC_CCR_VREFEN_Pos) /*!< 0x00400000 */ +#define ADC_CCR_VREFEN ADC_CCR_VREFEN_Msk /*!< VREFINT enable */ +#define ADC_CCR_TSEN_Pos (23U) +#define ADC_CCR_TSEN_Msk (0x1UL << ADC_CCR_TSEN_Pos) /*!< 0x00800000 */ +#define ADC_CCR_TSEN ADC_CCR_TSEN_Msk /*!< Temperature sensor enable */ +#define ADC_CCR_VBATEN_Pos (24U) +#define ADC_CCR_VBATEN_Msk (0x1UL << ADC_CCR_VBATEN_Pos) /*!< 0x01000000 */ +#define ADC_CCR_VBATEN ADC_CCR_VBATEN_Msk /*!< VBAT enable */ + +/******************** Bit definition for ADC_CDR register *******************/ +#define ADC_CDR_RDATA_MST_Pos (0U) +#define ADC_CDR_RDATA_MST_Msk (0xFFFFUL << ADC_CDR_RDATA_MST_Pos) /*!< 0x0000FFFF */ +#define ADC_CDR_RDATA_MST ADC_CDR_RDATA_MST_Msk /*!< ADC multimode master group regular conversion data */ + +#define ADC_CDR_RDATA_SLV_Pos (16U) +#define ADC_CDR_RDATA_SLV_Msk (0xFFFFUL << ADC_CDR_RDATA_SLV_Pos) /*!< 0xFFFF0000 */ +#define ADC_CDR_RDATA_SLV ADC_CDR_RDATA_SLV_Msk /*!< ADC multimode slave group regular conversion data */ + +/******************** Bit definition for ADC_CDR2 register ******************/ +#define ADC_CDR2_RDATA_ALT_Pos (0U) +#define ADC_CDR2_RDATA_ALT_Msk (0xFFFFFFFFUL << ADC_CDR2_RDATA_ALT_Pos) /*!< 0xFFFFFFFF */ +#define ADC_CDR2_RDATA_ALT ADC_CDR2_RDATA_ALT_Msk /*!< Regular data of the master/slave alternated ADCs */ + + +/******************************************************************************/ +/* */ +/* VREFBUF */ +/* */ +/******************************************************************************/ +/******************* Bit definition for VREFBUF_CSR register ****************/ +#define VREFBUF_CSR_ENVR_Pos (0U) +#define VREFBUF_CSR_ENVR_Msk (0x1UL << VREFBUF_CSR_ENVR_Pos) /*!< 0x00000001 */ +#define VREFBUF_CSR_ENVR VREFBUF_CSR_ENVR_Msk /*!*/ +#define DAC_CR_CEN1_Pos (14U) +#define DAC_CR_CEN1_Msk (0x1UL << DAC_CR_CEN1_Pos) /*!< 0x00004000 */ +#define DAC_CR_CEN1 DAC_CR_CEN1_Msk /*!*/ + +#define DAC_CR_EN2_Pos (16U) +#define DAC_CR_EN2_Msk (0x1UL << DAC_CR_EN2_Pos) /*!< 0x00010000 */ +#define DAC_CR_EN2 DAC_CR_EN2_Msk /*!*/ +#define DAC_CR_CEN2_Pos (30U) +#define DAC_CR_CEN2_Msk (0x1UL << DAC_CR_CEN2_Pos) /*!< 0x40000000 */ +#define DAC_CR_CEN2 DAC_CR_CEN2_Msk /*!*/ + +/***************** Bit definition for DAC_SWTRIGR register ******************/ +#define DAC_SWTRIGR_SWTRIG1_Pos (0U) +#define DAC_SWTRIGR_SWTRIG1_Msk (0x1UL << DAC_SWTRIGR_SWTRIG1_Pos) /*!< 0x00000001 */ +#define DAC_SWTRIGR_SWTRIG1 DAC_SWTRIGR_SWTRIG1_Msk /*!
© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.
+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32h7xx + * @{ + */ + +#ifndef STM32H7xx_H +#define STM32H7xx_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Library_configuration_section + * @{ + */ + +/** + * @brief STM32 Family + */ +#if !defined (STM32H7) +#define STM32H7 +#endif /* STM32H7 */ + + +/* Uncomment the line below according to the target STM32H7 device used in your + application + */ + +/* #if !defined (STM32H743xx) && !defined (STM32H753xx) && !defined (STM32H750xx) && !defined (STM32H742xx) && \ + !defined (STM32H745xx) && !defined (STM32H755xx) && !defined (STM32H747xx) && !defined (STM32H757xx) && \ + !defined (STM32H7A3xx) && !defined (STM32H7A3xxQ) && !defined (STM32H7B3xx) && !defined (STM32H7B3xxQ) && !defined (STM32H7B0xx) && !defined (STM32H7B0xxQ) && \ + !defined (STM32H735xx) && !defined (STM32H733xx) && !defined (STM32H730xx) && !defined (STM32H730xxQ) && !defined (STM32H725xx) && !defined (STM32H723xx) */ + /* #define STM32H742xx */ /*!< STM32H742VI, STM32H742ZI, STM32H742AI, STM32H742II, STM32H742BI, STM32H742XI Devices */ + /* #define STM32H743xx */ /*!< STM32H743VI, STM32H743ZI, STM32H743AI, STM32H743II, STM32H743BI, STM32H743XI Devices */ + /* #define STM32H753xx */ /*!< STM32H753VI, STM32H753ZI, STM32H753AI, STM32H753II, STM32H753BI, STM32H753XI Devices */ + /* #define STM32H750xx */ /*!< STM32H750V, STM32H750I, STM32H750X Devices */ + /* #define STM32H747xx */ /*!< STM32H747ZI, STM32H747AI, STM32H747II, STM32H747BI, STM32H747XI Devices */ + /* #define STM32H757xx */ /*!< STM32H757ZI, STM32H757AI, STM32H757II, STM32H757BI, STM32H757XI Devices */ + /* #define STM32H745xx */ /*!< STM32H745ZI, STM32H745II, STM32H745BI, STM32H745XI Devices */ + /* #define STM32H755xx */ /*!< STM32H755ZI, STM32H755II, STM32H755BI, STM32H755XI Devices */ + /* #define STM32H7B0xx */ /*!< STM32H7B0ABIxQ, STM32H7B0IBTx, STM32H7B0RBTx, STM32H7B0VBTx, STM32H7B0ZBTx, STM32H7B0IBKxQ */ + /* #define STM32H7A3xx */ /*!< STM32H7A3IIK6, STM32H7A3IIT6, STM32H7A3NIH6, STM32H7A3RIT6, STM32H7A3VIH6, STM32H7A3VIT6, STM32H7A3ZIT6 */ + /* #define STM32H7A3xxQ */ /*!< STM32H7A3QIY6Q, STM32H7A3IIK6Q, STM32H7A3IIT6Q, STM32H7A3LIH6Q, STM32H7A3VIH6Q, STM32H7A3VIT6Q, STM32H7A3AII6Q, STM32H7A3ZIT6Q */ + /* #define STM32H7B3xx */ /*!< STM32H7B3IIK6, STM32H7B3IIT6, STM32H7B3NIH6, STM32H7B3RIT6, STM32H7B3VIH6, STM32H7B3VIT6, STM32H7B3ZIT6 */ + /* #define STM32H7B3xxQ */ /*!< STM32H7B3QIY6Q, STM32H7B3IIK6Q, STM32H7B3IIT6Q, STM32H7B3LIH6Q, STM32H7B3VIH6Q, STM32H7B3VIT6Q, STM32H7B3AII6Q, STM32H7B3ZIT6Q */ + /* #define STM32H735xx */ /*!< STM32H735AGI6, STM32H735IGK6, STM32H735RGV6, STM32H735VGT6, STM32H735VGY6, STM32H735ZGT6 Devices */ + /* #define STM32H733xx */ /*!< STM32H733VGH6, STM32H733VGT6, STM32H733ZGI6, STM32H733ZGT6, Devices */ + /* #define STM32H730xx */ /*!< STM32H730VBH6, STM32H730VBT6, STM32H730ZBT6, STM32H730ZBI6 Devices */ + /* #define STM32H730xxQ */ /*!< STM32H730IBT6Q, STM32H730ABI6Q, STM32H730IBK6Q Devices */ + /* #define STM32H725xx */ /*!< STM32H725AGI6, STM32H725IGK6, STM32H725IGT6, STM32H725RGV6, STM32H725VGT6, STM32H725VGY6, STM32H725ZGT6, STM32H725REV6, SM32H725VET6, STM32H725ZET6, STM32H725AEI6, STM32H725IET6, STM32H725IEK6 Devices */ + /* #define STM32H723xx */ /*!< STM32H723VGH6, STM32H723VGT6, STM32H723ZGI6, STM32H723ZGT6, STM32H723VET6, STM32H723VEH6, STM32H723ZET6, STM32H723ZEI6 Devices */ +/* #endif */ + +/* Tip: To avoid modifying this file each time you need to switch between these + devices, you can define the device in your toolchain compiler preprocessor. + */ + +#if defined(DUAL_CORE) && !defined(CORE_CM4) && !defined(CORE_CM7) + #error "Dual core device, please select CORE_CM4 or CORE_CM7" +#endif + +#if !defined (USE_HAL_DRIVER) +/** + * @brief Comment the line below if you will not use the peripherals drivers. + In this case, these drivers will not be included and the application code will + be based on direct access to peripherals registers + */ + /*#define USE_HAL_DRIVER */ +#endif /* USE_HAL_DRIVER */ + +/** + * @brief CMSIS Device version number V1.10.0 + */ +#define __STM32H7xx_CMSIS_DEVICE_VERSION_MAIN (0x01) /*!< [31:24] main version */ +#define __STM32H7xx_CMSIS_DEVICE_VERSION_SUB1 (0x0A) /*!< [23:16] sub1 version */ +#define __STM32H7xx_CMSIS_DEVICE_VERSION_SUB2 (0x00) /*!< [15:8] sub2 version */ +#define __STM32H7xx_CMSIS_DEVICE_VERSION_RC (0x00) /*!< [7:0] release candidate */ +#define __STM32H7xx_CMSIS_DEVICE_VERSION ((__STM32H7xx_CMSIS_DEVICE_VERSION_MAIN << 24)\ + |(__STM32H7xx_CMSIS_DEVICE_VERSION_SUB1 << 16)\ + |(__STM32H7xx_CMSIS_DEVICE_VERSION_SUB2 << 8 )\ + |(__STM32H7xx_CMSIS_DEVICE_VERSION_RC)) + +/** + * @} + */ + +/** @addtogroup Device_Included + * @{ + */ + +#if defined(STM32H743xx) + #include "stm32h743xx.h" +// #elif defined(STM32H753xx) +// #include "stm32h753xx.h" +// #elif defined(STM32H750xx) +// #include "stm32h750xx.h" +// #elif defined(STM32H742xx) +// #include "stm32h742xx.h" +// #elif defined(STM32H745xx) +// #include "stm32h745xx.h" +// #elif defined(STM32H755xx) +// #include "stm32h755xx.h" +// #elif defined(STM32H747xx) +// #include "stm32h747xx.h" +// #elif defined(STM32H757xx) +// #include "stm32h757xx.h" +// #elif defined(STM32H7B0xx) +// #include "stm32h7b0xx.h" +// #elif defined(STM32H7B0xxQ) +// #include "stm32h7b0xxq.h" +// #elif defined(STM32H7A3xx) +// #include "stm32h7a3xx.h" +// #elif defined(STM32H7B3xx) +// #include "stm32h7b3xx.h" +// #elif defined(STM32H7A3xxQ) +// #include "stm32h7a3xxq.h" +// #elif defined(STM32H7B3xxQ) +// #include "stm32h7b3xxq.h" +#elif defined(STM32H735xx) + #include "stm32h735xx.h" +// #elif defined(STM32H733xx) +// #include "stm32h733xx.h" +// #elif defined(STM32H730xx) +// #include "stm32h730xx.h" +// #elif defined(STM32H730xxQ) +// #include "stm32h730xxq.h" +#elif defined(STM32H725xx) + #include "stm32h725xx.h" +// #elif defined(STM32H723xx) +// #include "stm32h723xx.h" +#else + #error "Please select first the target STM32H7xx device used in your application (in stm32h7xx.h file)" +#endif + +/** + * @} + */ + +/** @addtogroup Exported_types + * @{ + */ +typedef enum +{ + RESET = 0, + SET = !RESET +} FlagStatus, ITStatus; + +typedef enum +{ + DISABLE = 0, + ENABLE = !DISABLE +} FunctionalState; +#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE)) + +typedef enum +{ + SUCCESS = 0, + ERROR = !SUCCESS +} ErrorStatus; + +/** + * @} + */ + + +/** @addtogroup Exported_macros + * @{ + */ +#define SET_BIT(REG, BIT) ((REG) |= (BIT)) + +#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + +#define READ_BIT(REG, BIT) ((REG) & (BIT)) + +#define CLEAR_REG(REG) ((REG) = (0x0)) + +#define WRITE_REG(REG, VAL) ((REG) = (VAL)) + +#define READ_REG(REG) ((REG)) + +#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK))) + +#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL))) + + +/** + * @} + */ + +#if defined (USE_HAL_DRIVER) + #include "stm32h7xx_hal.h" +#endif /* USE_HAL_DRIVER */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* STM32H7xx_H */ +/** + * @} + */ + +/** + * @} + */ + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32h7/inc/stm32h7xx_hal_def.h b/panda/board/stm32h7/inc/stm32h7xx_hal_def.h new file mode 100644 index 0000000..3854b1c --- /dev/null +++ b/panda/board/stm32h7/inc/stm32h7xx_hal_def.h @@ -0,0 +1,221 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_def.h + * @author MCD Application Team + * @brief This file contains HAL common defines, enumeration, macros and + * structures definitions. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_DEF +#define STM32H7xx_HAL_DEF + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx.h" +//#include "Legacy/stm32_hal_legacy.h" +//#include +//#include + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief HAL Status structures definition + */ +typedef enum +{ + HAL_OK = 0x00, + HAL_ERROR = 0x01, + HAL_BUSY = 0x02, + HAL_TIMEOUT = 0x03 +} HAL_StatusTypeDef; + +/** + * @brief HAL Lock structures definition + */ +typedef enum +{ + HAL_UNLOCKED = 0x00, + HAL_LOCKED = 0x01 +} HAL_LockTypeDef; + +/* Exported macro ------------------------------------------------------------*/ + +#define HAL_MAX_DELAY 0xFFFFFFFFU + +#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) == (BIT)) +#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == 0U) + +#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \ + do{ \ + (__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \ + (__DMA_HANDLE__).Parent = (__HANDLE__); \ + } while(0) + +#define UNUSED(x) ((void)(x)) + +/** @brief Reset the Handle's State field. + * @param __HANDLE__: specifies the Peripheral Handle. + * @note This macro can be used for the following purpose: + * - When the Handle is declared as local variable; before passing it as parameter + * to HAL_PPP_Init() for the first time, it is mandatory to use this macro + * to set to 0 the Handle's "State" field. + * Otherwise, "State" field may have any random value and the first time the function + * HAL_PPP_Init() is called, the low level hardware initialization will be missed + * (i.e. HAL_PPP_MspInit() will not be executed). + * - When there is a need to reconfigure the low level hardware: instead of calling + * HAL_PPP_DeInit() then HAL_PPP_Init(), user can make a call to this macro then HAL_PPP_Init(). + * In this later function, when the Handle's "State" field is set to 0, it will execute the function + * HAL_PPP_MspInit() which will reconfigure the low level hardware. + * @retval None + */ +#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0) + +#if (USE_RTOS == 1) + #error " USE_RTOS should be 0 in the current HAL release " +#else + #define __HAL_LOCK(__HANDLE__) \ + do{ \ + if((__HANDLE__)->Lock == HAL_LOCKED) \ + { \ + return HAL_BUSY; \ + } \ + else \ + { \ + (__HANDLE__)->Lock = HAL_LOCKED; \ + } \ + }while (0) + + #define __HAL_UNLOCK(__HANDLE__) \ + do{ \ + (__HANDLE__)->Lock = HAL_UNLOCKED; \ + }while (0) +#endif /* USE_RTOS */ + + +#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */ + #ifndef __weak + #define __weak __attribute__((weak)) + #endif + #ifndef __packed + #define __packed __attribute__((packed)) + #endif +#elif defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */ + #ifndef __weak + #define __weak __attribute__((weak)) + #endif /* __weak */ + #ifndef __packed + #define __packed __attribute__((__packed__)) + #endif /* __packed */ +#endif /* __GNUC__ */ + + +/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */ +#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif +#elif defined ( __GNUC__ ) && !defined (__CC_ARM) /* GNU Compiler */ + #ifndef __ALIGN_END + #define __ALIGN_END __attribute__ ((aligned (4))) + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #define __ALIGN_BEGIN + #endif /* __ALIGN_BEGIN */ +#else + #ifndef __ALIGN_END + #define __ALIGN_END + #endif /* __ALIGN_END */ + #ifndef __ALIGN_BEGIN + #if defined (__CC_ARM) /* ARM Compiler V5 */ + #define __ALIGN_BEGIN __align(4) + #elif defined (__ICCARM__) /* IAR Compiler */ + #define __ALIGN_BEGIN + #endif /* __CC_ARM */ + #endif /* __ALIGN_BEGIN */ +#endif /* __GNUC__ */ + +/* Macro to get variable aligned on 32-bytes,needed for cache maintenance purpose */ +#if defined (__GNUC__) /* GNU Compiler */ + #define ALIGN_32BYTES(buf) buf __attribute__ ((aligned (32))) +#elif defined (__ICCARM__) /* IAR Compiler */ + #define ALIGN_32BYTES(buf) _Pragma("data_alignment=32") buf +#elif defined (__CC_ARM) /* ARM Compiler */ + #define ALIGN_32BYTES(buf) __align(32) buf +#endif + +/** + * @brief __RAM_FUNC definition + */ +#if defined ( __CC_ARM ) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) +/* ARM Compiler V4/V5 and V6 + -------------------------- + RAM functions are defined using the toolchain options. + Functions that are executed in RAM should reside in a separate source module. + Using the 'Options for File' dialog you can simply change the 'Code / Const' + area of a module to a memory space in physical RAM. + Available memory areas are declared in the 'Target' tab of the 'Options for Target' + dialog. +*/ +#define __RAM_FUNC + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- + RAM functions are defined using a specific toolchain keyword "__ramfunc". +*/ +#define __RAM_FUNC __ramfunc + +#elif defined ( __GNUC__ ) +/* GNU Compiler + ------------ + RAM functions are defined using a specific toolchain attribute + "__attribute__((section(".RamFunc")))". +*/ +#define __RAM_FUNC __attribute__((section(".RamFunc"))) + +#endif + +/** + * @brief __NOINLINE definition + */ +#if defined ( __CC_ARM ) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)) || defined ( __GNUC__ ) +/* ARM V4/V5 and V6 & GNU Compiler + ------------------------------- +*/ +#define __NOINLINE __attribute__ ( (noinline) ) + +#elif defined ( __ICCARM__ ) +/* ICCARM Compiler + --------------- +*/ +#define __NOINLINE _Pragma("optimize = no_inline") + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_DEF */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32h7/inc/stm32h7xx_hal_gpio_ex.h b/panda/board/stm32h7/inc/stm32h7xx_hal_gpio_ex.h new file mode 100644 index 0000000..14f576a --- /dev/null +++ b/panda/board/stm32h7/inc/stm32h7xx_hal_gpio_ex.h @@ -0,0 +1,489 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_gpio_ex.h + * @author MCD Application Team + * @brief Header file of GPIO HAL Extension module. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_GPIO_EX_H +#define STM32H7xx_HAL_GPIO_EX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal_def.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @addtogroup GPIOEx GPIOEx + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ + +/* Exported constants --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_Alternate_function_selection GPIO Alternate Function Selection + * @{ + */ + +/** + * @brief AF 0 selection + */ +#define GPIO_AF0_RTC_50Hz ((uint8_t)0x00) /* RTC_50Hz Alternate Function mapping */ +#define GPIO_AF0_MCO ((uint8_t)0x00) /* MCO (MCO1 and MCO2) Alternate Function mapping */ +#define GPIO_AF0_SWJ ((uint8_t)0x00) /* SWJ (SWD and JTAG) Alternate Function mapping */ +#define GPIO_AF0_LCDBIAS ((uint8_t)0x00) /* LCDBIAS Alternate Function mapping */ +#define GPIO_AF0_TRACE ((uint8_t)0x00) /* TRACE Alternate Function mapping */ +#if defined (PWR_CPUCR_PDDS_D2) /* PWR D1 and D2 domains exists */ +#define GPIO_AF0_C1DSLEEP ((uint8_t)0x00) /* Cortex-M7 Deep Sleep Alternate Function mapping : available on STM32H7 Rev.B and above */ +#define GPIO_AF0_C1SLEEP ((uint8_t)0x00) /* Cortex-M7 Sleep Alternate Function mapping : available on STM32H7 Rev.B and above */ +#define GPIO_AF0_D1PWREN ((uint8_t)0x00) /* Domain 1 PWR enable Alternate Function mapping : available on STM32H7 Rev.B and above */ +#define GPIO_AF0_D2PWREN ((uint8_t)0x00) /* Domain 2 PWR enable Alternate Function mapping : available on STM32H7 Rev.B and above */ +#if defined(DUAL_CORE) +#define GPIO_AF0_C2DSLEEP ((uint8_t)0x00) /* Cortex-M4 Deep Sleep Alternate Function mapping : available on STM32H7 Rev.B and above */ +#define GPIO_AF0_C2SLEEP ((uint8_t)0x00) /* Cortex-M4 Sleep Alternate Function mapping : available on STM32H7 Rev.B and above */ +#endif /* DUAL_CORE */ +#endif /* PWR_CPUCR_PDDS_D2 */ + +/** + * @brief AF 1 selection + */ +#define GPIO_AF1_TIM1 ((uint8_t)0x01) /* TIM1 Alternate Function mapping */ +#define GPIO_AF1_TIM2 ((uint8_t)0x01) /* TIM2 Alternate Function mapping */ +#define GPIO_AF1_TIM16 ((uint8_t)0x01) /* TIM16 Alternate Function mapping */ +#define GPIO_AF1_TIM17 ((uint8_t)0x01) /* TIM17 Alternate Function mapping */ +#define GPIO_AF1_LPTIM1 ((uint8_t)0x01) /* LPTIM1 Alternate Function mapping */ +#if defined(HRTIM1) +#define GPIO_AF1_HRTIM1 ((uint8_t)0x01) /* HRTIM1 Alternate Function mapping */ +#endif /* HRTIM1 */ +#if defined(SAI4) +#define GPIO_AF1_SAI4 ((uint8_t)0x01) /* SAI4 Alternate Function mapping : available on STM32H72xxx/STM32H73xxx */ +#endif /* SAI4 */ +#define GPIO_AF1_FMC ((uint8_t)0x01) /* FMC Alternate Function mapping : available on STM32H72xxx/STM32H73xxx */ + + +/** + * @brief AF 2 selection + */ +#define GPIO_AF2_TIM3 ((uint8_t)0x02) /* TIM3 Alternate Function mapping */ +#define GPIO_AF2_TIM4 ((uint8_t)0x02) /* TIM4 Alternate Function mapping */ +#define GPIO_AF2_TIM5 ((uint8_t)0x02) /* TIM5 Alternate Function mapping */ +#define GPIO_AF2_TIM12 ((uint8_t)0x02) /* TIM12 Alternate Function mapping */ +#define GPIO_AF2_SAI1 ((uint8_t)0x02) /* SAI1 Alternate Function mapping */ +#if defined(HRTIM1) +#define GPIO_AF2_HRTIM1 ((uint8_t)0x02) /* HRTIM1 Alternate Function mapping */ +#endif /* HRTIM1 */ +#define GPIO_AF2_TIM15 ((uint8_t)0x02) /* TIM15 Alternate Function mapping : available on STM32H7A3xxx/STM32H7B3xxx/STM32H7B0xxx and STM32H72xxx/STM32H73xxx */ +#if defined(FDCAN3) +#define GPIO_AF2_FDCAN3 ((uint8_t)0x02) /* FDCAN3 Alternate Function mapping */ +#endif /*FDCAN3*/ + +/** + * @brief AF 3 selection + */ +#define GPIO_AF3_TIM8 ((uint8_t)0x03) /* TIM8 Alternate Function mapping */ +#define GPIO_AF3_LPTIM2 ((uint8_t)0x03) /* LPTIM2 Alternate Function mapping */ +#define GPIO_AF3_DFSDM1 ((uint8_t)0x03) /* DFSDM Alternate Function mapping */ +#define GPIO_AF3_LPTIM3 ((uint8_t)0x03) /* LPTIM3 Alternate Function mapping */ +#define GPIO_AF3_LPTIM4 ((uint8_t)0x03) /* LPTIM4 Alternate Function mapping */ +#define GPIO_AF3_LPTIM5 ((uint8_t)0x03) /* LPTIM5 Alternate Function mapping */ +#define GPIO_AF3_LPUART ((uint8_t)0x03) /* LPUART Alternate Function mapping */ +#if defined(OCTOSPIM) +#define GPIO_AF3_OCTOSPIM_P1 ((uint8_t)0x03) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#define GPIO_AF3_OCTOSPIM_P2 ((uint8_t)0x03) /* OCTOSPI Manager Port 2 Alternate Function mapping */ +#endif /* OCTOSPIM */ +#if defined(HRTIM1) +#define GPIO_AF3_HRTIM1 ((uint8_t)0x03) /* HRTIM1 Alternate Function mapping */ +#endif /* HRTIM1 */ +#define GPIO_AF3_LTDC ((uint8_t)0x03) /* LTDC Alternate Function mapping : available on STM32H72xxx/STM32H73xxx */ + +/** + * @brief AF 4 selection + */ +#define GPIO_AF4_I2C1 ((uint8_t)0x04) /* I2C1 Alternate Function mapping */ +#define GPIO_AF4_I2C2 ((uint8_t)0x04) /* I2C2 Alternate Function mapping */ +#define GPIO_AF4_I2C3 ((uint8_t)0x04) /* I2C3 Alternate Function mapping */ +#define GPIO_AF4_I2C4 ((uint8_t)0x04) /* I2C4 Alternate Function mapping */ +#if defined(I2C5) +#define GPIO_AF4_I2C5 ((uint8_t)0x04) /* I2C5 Alternate Function mapping */ +#endif /* I2C5*/ +#define GPIO_AF4_TIM15 ((uint8_t)0x04) /* TIM15 Alternate Function mapping */ +#define GPIO_AF4_CEC ((uint8_t)0x04) /* CEC Alternate Function mapping */ +#define GPIO_AF4_LPTIM2 ((uint8_t)0x04) /* LPTIM2 Alternate Function mapping */ +#define GPIO_AF4_USART1 ((uint8_t)0x04) /* USART1 Alternate Function mapping */ +#if defined(USART10) +#define GPIO_AF4_USART10 ((uint8_t)0x04) /* USART10 Alternate Function mapping : available on STM32H72xxx/STM32H73xxx */ +#endif /*USART10*/ +#define GPIO_AF4_DFSDM1 ((uint8_t)0x04) /* DFSDM Alternate Function mapping */ +#if defined(DFSDM2_BASE) +#define GPIO_AF4_DFSDM2 ((uint8_t)0x04) /* DFSDM2 Alternate Function mapping */ +#endif /* DFSDM2_BASE */ +#define GPIO_AF4_DCMI ((uint8_t)0x04) /* DCMI Alternate Function mapping : available on STM32H7A3xxx/STM32H7B3xxx/STM32H7B0xxx and STM32H72xxx/STM32H73xxx */ +#if defined(PSSI) +#define GPIO_AF4_PSSI ((uint8_t)0x04) /* PSSI Alternate Function mapping */ +#endif /* PSSI */ +#if defined(OCTOSPIM) +#define GPIO_AF4_OCTOSPIM_P1 ((uint8_t)0x04) /* OCTOSPI Manager Port 1 Alternate Function mapping : available on STM32H72xxx/STM32H73xxx */ +#endif /* OCTOSPIM */ + +/** + * @brief AF 5 selection + */ +#define GPIO_AF5_SPI1 ((uint8_t)0x05) /* SPI1 Alternate Function mapping */ +#define GPIO_AF5_SPI2 ((uint8_t)0x05) /* SPI2 Alternate Function mapping */ +#define GPIO_AF5_SPI3 ((uint8_t)0x05) /* SPI3 Alternate Function mapping */ +#define GPIO_AF5_SPI4 ((uint8_t)0x05) /* SPI4 Alternate Function mapping */ +#define GPIO_AF5_SPI5 ((uint8_t)0x05) /* SPI5 Alternate Function mapping */ +#define GPIO_AF5_SPI6 ((uint8_t)0x05) /* SPI6 Alternate Function mapping */ +#define GPIO_AF5_CEC ((uint8_t)0x05) /* CEC Alternate Function mapping */ +#if defined(FDCAN3) +#define GPIO_AF5_FDCAN3 ((uint8_t)0x05) /* FDCAN3 Alternate Function mapping */ +#endif /*FDCAN3*/ + +/** + * @brief AF 6 selection + */ +#define GPIO_AF6_SPI2 ((uint8_t)0x06) /* SPI2 Alternate Function mapping */ +#define GPIO_AF6_SPI3 ((uint8_t)0x06) /* SPI3 Alternate Function mapping */ +#define GPIO_AF6_SAI1 ((uint8_t)0x06) /* SAI1 Alternate Function mapping */ +#define GPIO_AF6_I2C4 ((uint8_t)0x06) /* I2C4 Alternate Function mapping */ +#if defined(I2C5) +#define GPIO_AF6_I2C5 ((uint8_t)0x06) /* I2C5 Alternate Function mapping */ +#endif /* I2C5*/ +#define GPIO_AF6_DFSDM1 ((uint8_t)0x06) /* DFSDM Alternate Function mapping */ +#define GPIO_AF6_UART4 ((uint8_t)0x06) /* UART4 Alternate Function mapping */ +#if defined(DFSDM2_BASE) +#define GPIO_AF6_DFSDM2 ((uint8_t)0x06) /* DFSDM2 Alternate Function mapping */ +#endif /* DFSDM2_BASE */ +#if defined(SAI3) +#define GPIO_AF6_SAI3 ((uint8_t)0x06) /* SAI3 Alternate Function mapping */ +#endif /* SAI3 */ +#if defined(OCTOSPIM) +#define GPIO_AF6_OCTOSPIM_P1 ((uint8_t)0x06) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#endif /* OCTOSPIM */ + +/** + * @brief AF 7 selection + */ +#define GPIO_AF7_SPI2 ((uint8_t)0x07) /* SPI2 Alternate Function mapping */ +#define GPIO_AF7_SPI3 ((uint8_t)0x07) /* SPI3 Alternate Function mapping */ +#define GPIO_AF7_SPI6 ((uint8_t)0x07) /* SPI6 Alternate Function mapping */ +#define GPIO_AF7_USART1 ((uint8_t)0x07) /* USART1 Alternate Function mapping */ +#define GPIO_AF7_USART2 ((uint8_t)0x07) /* USART2 Alternate Function mapping */ +#define GPIO_AF7_USART3 ((uint8_t)0x07) /* USART3 Alternate Function mapping */ +#define GPIO_AF7_USART6 ((uint8_t)0x07) /* USART6 Alternate Function mapping */ +#define GPIO_AF7_UART7 ((uint8_t)0x07) /* UART7 Alternate Function mapping */ +#define GPIO_AF7_SDMMC1 ((uint8_t)0x07) /* SDMMC1 Alternate Function mapping */ + +/** + * @brief AF 8 selection + */ +#define GPIO_AF8_SPI6 ((uint8_t)0x08) /* SPI6 Alternate Function mapping */ +#if defined(SAI2) +#define GPIO_AF8_SAI2 ((uint8_t)0x08) /* SAI2 Alternate Function mapping */ +#endif /*SAI2*/ +#define GPIO_AF8_UART4 ((uint8_t)0x08) /* UART4 Alternate Function mapping */ +#define GPIO_AF8_UART5 ((uint8_t)0x08) /* UART5 Alternate Function mapping */ +#define GPIO_AF8_UART8 ((uint8_t)0x08) /* UART8 Alternate Function mapping */ +#define GPIO_AF8_SPDIF ((uint8_t)0x08) /* SPDIF Alternate Function mapping */ +#define GPIO_AF8_LPUART ((uint8_t)0x08) /* LPUART Alternate Function mapping */ +#define GPIO_AF8_SDMMC1 ((uint8_t)0x08) /* SDMMC1 Alternate Function mapping */ +#if defined(SAI4) +#define GPIO_AF8_SAI4 ((uint8_t)0x08) /* SAI4 Alternate Function mapping */ +#endif /* SAI4 */ + +/** + * @brief AF 9 selection + */ +#define GPIO_AF9_FDCAN1 ((uint8_t)0x09) /* FDCAN1 Alternate Function mapping */ +#define GPIO_AF9_FDCAN2 ((uint8_t)0x09) /* FDCAN2 Alternate Function mapping */ +#define GPIO_AF9_TIM13 ((uint8_t)0x09) /* TIM13 Alternate Function mapping */ +#define GPIO_AF9_TIM14 ((uint8_t)0x09) /* TIM14 Alternate Function mapping */ +#define GPIO_AF9_SDMMC2 ((uint8_t)0x09) /* SDMMC2 Alternate Function mapping */ +#define GPIO_AF9_LTDC ((uint8_t)0x09) /* LTDC Alternate Function mapping */ +#define GPIO_AF9_SPDIF ((uint8_t)0x09) /* SPDIF Alternate Function mapping */ +#define GPIO_AF9_FMC ((uint8_t)0x09) /* FMC Alternate Function mapping */ +#if defined(QUADSPI) +#define GPIO_AF9_QUADSPI ((uint8_t)0x09) /* QUADSPI Alternate Function mapping */ +#endif /* QUADSPI */ +#if defined(SAI4) +#define GPIO_AF9_SAI4 ((uint8_t)0x09) /* SAI4 Alternate Function mapping */ +#endif /* SAI4 */ +#if defined(OCTOSPIM) +#define GPIO_AF9_OCTOSPIM_P1 ((uint8_t)0x09) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#define GPIO_AF9_OCTOSPIM_P2 ((uint8_t)0x09) /* OCTOSPI Manager Port 2 Alternate Function mapping */ +#endif /* OCTOSPIM */ + +/** + * @brief AF 10 selection + */ +#if defined(SAI2) +#define GPIO_AF10_SAI2 ((uint8_t)0x0A) /* SAI2 Alternate Function mapping */ +#endif /*SAI2*/ +#define GPIO_AF10_SDMMC2 ((uint8_t)0x0A) /* SDMMC2 Alternate Function mapping */ +#if defined(USB2_OTG_FS) +#define GPIO_AF10_OTG2_FS ((uint8_t)0x0A) /* OTG2_FS Alternate Function mapping */ +#endif /*USB2_OTG_FS*/ +#define GPIO_AF10_COMP1 ((uint8_t)0x0A) /* COMP1 Alternate Function mapping */ +#define GPIO_AF10_COMP2 ((uint8_t)0x0A) /* COMP2 Alternate Function mapping */ +#if defined(LTDC) +#define GPIO_AF10_LTDC ((uint8_t)0x0A) /* LTDC Alternate Function mapping */ +#endif /*LTDC*/ +#define GPIO_AF10_CRS_SYNC ((uint8_t)0x0A) /* CRS Sync Alternate Function mapping : available on STM32H7 Rev.B and above */ +#if defined(QUADSPI) +#define GPIO_AF10_QUADSPI ((uint8_t)0x0A) /* QUADSPI Alternate Function mapping */ +#endif /* QUADSPI */ +#if defined(SAI4) +#define GPIO_AF10_SAI4 ((uint8_t)0x0A) /* SAI4 Alternate Function mapping */ +#endif /* SAI4 */ +#if !defined(USB2_OTG_FS) +#define GPIO_AF10_OTG1_FS ((uint8_t)0x0A) /* OTG1_FS Alternate Function mapping : available on STM32H7A3xxx/STM32H7B3xxx/STM32H7B0xxx and STM32H72xxx/STM32H73xxx */ +#endif /* !USB2_OTG_FS */ +#define GPIO_AF10_OTG1_HS ((uint8_t)0x0A) /* OTG1_HS Alternate Function mapping */ +#if defined(OCTOSPIM) +#define GPIO_AF10_OCTOSPIM_P1 ((uint8_t)0x0A) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#endif /* OCTOSPIM */ +#define GPIO_AF10_TIM8 ((uint8_t)0x0A) /* TIM8 Alternate Function mapping */ +#define GPIO_AF10_FMC ((uint8_t)0x0A) /* FMC Alternate Function mapping : available on STM32H7A3xxx/STM32H7B3xxx/STM32H7B0xxx and STM32H72xxx/STM32H73xxx */ + +/** + * @brief AF 11 selection + */ +#define GPIO_AF11_SWP ((uint8_t)0x0B) /* SWP Alternate Function mapping */ +#define GPIO_AF11_MDIOS ((uint8_t)0x0B) /* MDIOS Alternate Function mapping */ +#define GPIO_AF11_UART7 ((uint8_t)0x0B) /* UART7 Alternate Function mapping */ +#define GPIO_AF11_SDMMC2 ((uint8_t)0x0B) /* SDMMC2 Alternate Function mapping */ +#define GPIO_AF11_DFSDM1 ((uint8_t)0x0B) /* DFSDM1 Alternate Function mapping */ +#define GPIO_AF11_COMP1 ((uint8_t)0x0B) /* COMP1 Alternate Function mapping */ +#define GPIO_AF11_COMP2 ((uint8_t)0x0B) /* COMP2 Alternate Function mapping */ +#define GPIO_AF11_TIM1 ((uint8_t)0x0B) /* TIM1 Alternate Function mapping */ +#define GPIO_AF11_TIM8 ((uint8_t)0x0B) /* TIM8 Alternate Function mapping */ +#define GPIO_AF11_I2C4 ((uint8_t)0x0B) /* I2C4 Alternate Function mapping */ +#if defined(DFSDM2_BASE) +#define GPIO_AF11_DFSDM2 ((uint8_t)0x0B) /* DFSDM2 Alternate Function mapping */ +#endif /* DFSDM2_BASE */ +#if defined(USART10) +#define GPIO_AF11_USART10 ((uint8_t)0x0B) /* USART10 Alternate Function mapping */ +#endif /* USART10 */ +#if defined(UART9) +#define GPIO_AF11_UART9 ((uint8_t)0x0B) /* UART9 Alternate Function mapping */ +#endif /* UART9 */ +#if defined(ETH) +#define GPIO_AF11_ETH ((uint8_t)0x0B) /* ETH Alternate Function mapping */ +#endif /* ETH */ +#if defined(LTDC) +#define GPIO_AF11_LTDC ((uint8_t)0x0B) /* LTDC Alternate Function mapping : available on STM32H7A3xxx/STM32H7B3xxx/STM32H7B0xxx and STM32H72xxx/STM32H73xxx */ +#endif /*LTDC*/ +#if defined(OCTOSPIM) +#define GPIO_AF11_OCTOSPIM_P1 ((uint8_t)0x0B) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#endif /* OCTOSPIM */ + +/** + * @brief AF 12 selection + */ +#define GPIO_AF12_FMC ((uint8_t)0x0C) /* FMC Alternate Function mapping */ +#define GPIO_AF12_SDMMC1 ((uint8_t)0x0C) /* SDMMC1 Alternate Function mapping */ +#define GPIO_AF12_MDIOS ((uint8_t)0x0C) /* MDIOS Alternate Function mapping */ +#define GPIO_AF12_COMP1 ((uint8_t)0x0C) /* COMP1 Alternate Function mapping */ +#define GPIO_AF12_COMP2 ((uint8_t)0x0C) /* COMP2 Alternate Function mapping */ +#define GPIO_AF12_TIM1 ((uint8_t)0x0C) /* TIM1 Alternate Function mapping */ +#define GPIO_AF12_TIM8 ((uint8_t)0x0C) /* TIM8 Alternate Function mapping */ +#if defined(LTDC) +#define GPIO_AF12_LTDC ((uint8_t)0x0C) /* LTDC Alternate Function mapping */ +#endif /*LTDC*/ +#if defined(USB2_OTG_FS) +#define GPIO_AF12_OTG1_FS ((uint8_t)0x0C) /* OTG1_FS Alternate Function mapping */ +#endif /* USB2_OTG_FS */ +#if defined(OCTOSPIM) +#define GPIO_AF12_OCTOSPIM_P1 ((uint8_t)0x0C) /* OCTOSPI Manager Port 1 Alternate Function mapping */ +#endif /* OCTOSPIM */ + +/** + * @brief AF 13 selection + */ +#define GPIO_AF13_DCMI ((uint8_t)0x0D) /* DCMI Alternate Function mapping */ +#define GPIO_AF13_COMP1 ((uint8_t)0x0D) /* COMP1 Alternate Function mapping */ +#define GPIO_AF13_COMP2 ((uint8_t)0x0D) /* COMP2 Alternate Function mapping */ +#if defined(LTDC) +#define GPIO_AF13_LTDC ((uint8_t)0x0D) /* LTDC Alternate Function mapping */ +#endif /*LTDC*/ +#if defined(DSI) +#define GPIO_AF13_DSI ((uint8_t)0x0D) /* DSI Alternate Function mapping */ +#endif /* DSI */ +#if defined(PSSI) +#define GPIO_AF13_PSSI ((uint8_t)0x0D) /* PSSI Alternate Function mapping */ +#endif /* PSSI */ +#define GPIO_AF13_TIM1 ((uint8_t)0x0D) /* TIM1 Alternate Function mapping */ +#if defined(TIM23) +#define GPIO_AF13_TIM23 ((uint8_t)0x0D) /* TIM23 Alternate Function mapping */ +#endif /*TIM23*/ + +/** + * @brief AF 14 selection + */ +#define GPIO_AF14_LTDC ((uint8_t)0x0E) /* LTDC Alternate Function mapping */ +#define GPIO_AF14_UART5 ((uint8_t)0x0E) /* UART5 Alternate Function mapping */ +#if defined(TIM24) +#define GPIO_AF14_TIM24 ((uint8_t)0x0E) /* TIM24 Alternate Function mapping */ +#endif /*TIM24*/ + +/** + * @brief AF 15 selection + */ +#define GPIO_AF15_EVENTOUT ((uint8_t)0x0F) /* EVENTOUT Alternate Function mapping */ + +#define IS_GPIO_AF(AF) ((AF) <= (uint8_t)0x0F) + + + +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Macros GPIO Exported Macros + * @{ + */ +/** + * @} + */ + +/* Exported functions --------------------------------------------------------*/ +/** @defgroup GPIOEx_Exported_Functions GPIO Exported Functions + * @{ + */ +/** + * @} + */ +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Constants GPIO Private Constants + * @{ + */ + +/** + * @brief GPIO pin available on the platform + */ +/* Defines the available pins per GPIOs */ +#define GPIOA_PIN_AVAILABLE GPIO_PIN_All +#define GPIOB_PIN_AVAILABLE GPIO_PIN_All +#define GPIOC_PIN_AVAILABLE GPIO_PIN_All +#define GPIOD_PIN_AVAILABLE GPIO_PIN_All +#define GPIOE_PIN_AVAILABLE GPIO_PIN_All +#define GPIOF_PIN_AVAILABLE GPIO_PIN_All +#define GPIOG_PIN_AVAILABLE GPIO_PIN_All +#if defined(GPIOI) +#define GPIOI_PIN_AVAILABLE GPIO_PIN_All +#endif /*GPIOI*/ +#if defined(GPIOI) +#define GPIOJ_PIN_AVAILABLE GPIO_PIN_All +#else +#define GPIOJ_PIN_AVAILABLE (GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 ) +#endif /* GPIOI */ +#define GPIOH_PIN_AVAILABLE GPIO_PIN_All +#if defined(GPIOI) +#define GPIOK_PIN_AVAILABLE (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | \ + GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7) +#else +#define GPIOK_PIN_AVAILABLE (GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 ) +#endif /* GPIOI */ + +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Macros GPIO Private Macros + * @{ + */ +/** @defgroup GPIOEx_Get_Port_Index GPIO Get Port Index + * @{ + */ +#if defined(GPIOI) +#define GPIO_GET_INDEX(__GPIOx__) (((__GPIOx__) == (GPIOA))? 0UL :\ + ((__GPIOx__) == (GPIOB))? 1UL :\ + ((__GPIOx__) == (GPIOC))? 2UL :\ + ((__GPIOx__) == (GPIOD))? 3UL :\ + ((__GPIOx__) == (GPIOE))? 4UL :\ + ((__GPIOx__) == (GPIOF))? 5UL :\ + ((__GPIOx__) == (GPIOG))? 6UL :\ + ((__GPIOx__) == (GPIOH))? 7UL :\ + ((__GPIOx__) == (GPIOI))? 8UL :\ + ((__GPIOx__) == (GPIOJ))? 9UL : 10UL) +#else +#define GPIO_GET_INDEX(__GPIOx__) (((__GPIOx__) == (GPIOA))? 0UL :\ + ((__GPIOx__) == (GPIOB))? 1UL :\ + ((__GPIOx__) == (GPIOC))? 2UL :\ + ((__GPIOx__) == (GPIOD))? 3UL :\ + ((__GPIOx__) == (GPIOE))? 4UL :\ + ((__GPIOx__) == (GPIOF))? 5UL :\ + ((__GPIOx__) == (GPIOG))? 6UL :\ + ((__GPIOx__) == (GPIOH))? 7UL :\ + ((__GPIOx__) == (GPIOJ))? 9UL : 10UL) +#endif /* GPIOI */ + +/** + * @} + */ + +/** @defgroup GPIOEx_IS_Alternat_function_selection GPIO Check Alternate Function + * @{ + */ +/** + * @} + */ + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIOEx_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_GPIO_EX_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32h7/inc/system_stm32h7xx.h b/panda/board/stm32h7/inc/system_stm32h7xx.h new file mode 100644 index 0000000..dd75af6 --- /dev/null +++ b/panda/board/stm32h7/inc/system_stm32h7xx.h @@ -0,0 +1,105 @@ +/** + ****************************************************************************** + * @file system_stm32h7xx.h + * @author MCD Application Team + * @brief CMSIS Cortex-Mx Device System Source File for STM32H7xx devices. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32h7xx_system + * @{ + */ + +/** + * @brief Define to prevent recursive inclusion + */ +#ifndef SYSTEM_STM32H7XX_H +#define SYSTEM_STM32H7XX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** @addtogroup STM32H7xx_System_Includes + * @{ + */ + +/** + * @} + */ + + +/** @addtogroup STM32H7xx_System_Exported_types + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetSysClockFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock; then there + is no need to call the 2 first functions listed above, since SystemCoreClock + variable is updated automatically. + */ +extern uint32_t SystemCoreClock; /*!< System Domain1 Clock Frequency */ +extern uint32_t SystemD2Clock; /*!< System Domain2 Clock Frequency */ +extern const uint8_t D1CorePrescTable[16] ; /*!< D1CorePrescTable prescalers table values */ + +/** + * @} + */ + +/** @addtogroup STM32H7xx_System_Exported_Constants + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32H7xx_System_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32H7xx_System_Exported_Functions + * @{ + */ + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_STM32H7XX_H */ + +/** + * @} + */ + +/** + * @} + */ +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/panda/board/stm32h7/interrupt_handlers.h b/panda/board/stm32h7/interrupt_handlers.h new file mode 100644 index 0000000..fb1298d --- /dev/null +++ b/panda/board/stm32h7/interrupt_handlers.h @@ -0,0 +1,76 @@ +// ********************* Bare interrupt handlers ********************* +// Interrupts for STM32H7x5 + +void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);} +void PVD_AVD_IRQHandler(void) {handle_interrupt(PVD_AVD_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 EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);} +void TIM1_BRK_IRQHandler(void) {handle_interrupt(TIM1_BRK_IRQn);} +void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);} +void TIM1_TRG_COM_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_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 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 TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);} +void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);} +void SPI4_IRQHandler(void) {handle_interrupt(SPI4_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 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);} +void FDCAN1_IT0_IRQHandler(void) {handle_interrupt(FDCAN1_IT0_IRQn);} +void FDCAN1_IT1_IRQHandler(void) {handle_interrupt(FDCAN1_IT1_IRQn);} +void FDCAN2_IT0_IRQHandler(void) {handle_interrupt(FDCAN2_IT0_IRQn);} +void FDCAN2_IT1_IRQHandler(void) {handle_interrupt(FDCAN2_IT1_IRQn);} +void FDCAN3_IT0_IRQHandler(void) {handle_interrupt(FDCAN3_IT0_IRQn);} +void FDCAN3_IT1_IRQHandler(void) {handle_interrupt(FDCAN3_IT1_IRQn);} +void FDCAN_CAL_IRQHandler(void) {handle_interrupt(FDCAN_CAL_IRQn);} +void OTG_HS_EP1_OUT_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_OUT_IRQn);} +void OTG_HS_EP1_IN_IRQHandler(void) {handle_interrupt(OTG_HS_EP1_IN_IRQn);} +void OTG_HS_WKUP_IRQHandler(void) {handle_interrupt(OTG_HS_WKUP_IRQn);} +void OTG_HS_IRQHandler(void) {handle_interrupt(OTG_HS_IRQn);} +void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);} diff --git a/panda/board/stm32h7/lladc.h b/panda/board/stm32h7/lladc.h new file mode 100644 index 0000000..01342a4 --- /dev/null +++ b/panda/board/stm32h7/lladc.h @@ -0,0 +1,39 @@ + +void adc_init(void) { + ADC1->CR &= ~(ADC_CR_DEEPPWD); //Reset deep-power-down mode + ADC1->CR |= ADC_CR_ADVREGEN; // Enable ADC regulator + while(!(ADC1->ISR & ADC_ISR_LDORDY)); + + ADC1->CR &= ~(ADC_CR_ADCALDIF); // Choose single-ended calibration + ADC1->CR |= ADC_CR_ADCALLIN; // Lineriality calibration + ADC1->CR |= ADC_CR_ADCAL; // Start calibrtation + while((ADC1->CR & ADC_CR_ADCAL) != 0); + + ADC1->ISR |= ADC_ISR_ADRDY; + ADC1->CR |= ADC_CR_ADEN; + while(!(ADC1->ISR & ADC_ISR_ADRDY)); +} + +uint16_t adc_get_raw(uint8_t channel) { + uint16_t res = 0U; + ADC1->SQR1 &= ~(ADC_SQR1_L); + ADC1->SQR1 = ((uint32_t) channel << 6U); + + ADC1->SMPR1 = (0x2U << (channel * 3U)); + ADC1->PCSEL_RES0 = (0x1UL << channel); + ADC1->CFGR2 = (127U << ADC_CFGR2_OVSR_Pos) | (0x7U << ADC_CFGR2_OVSS_Pos) | ADC_CFGR2_ROVSE; + + ADC1->CR |= ADC_CR_ADSTART; + while (!(ADC1->ISR & ADC_ISR_EOC)); + + res = ADC1->DR; + + while (!(ADC1->ISR & ADC_ISR_EOS)); + ADC1->ISR |= ADC_ISR_EOS; + + return res; +} + +uint16_t adc_get_mV(uint8_t channel) { + return (adc_get_raw(channel) * current_board->avdd_mV) / 65535U; +} diff --git a/panda/board/stm32h7/lldac.h b/panda/board/stm32h7/lldac.h new file mode 100644 index 0000000..5726f62 --- /dev/null +++ b/panda/board/stm32h7/lldac.h @@ -0,0 +1,42 @@ +void dac_init(DAC_TypeDef *dac, uint8_t channel, bool dma) { + register_set(&dac->CR, 0U, 0xFFFFU); + register_set(&dac->MCR, 0U, 0xFFFFU); + + switch(channel) { + case 1: + if (dma) { + register_set_bits(&dac->CR, DAC_CR_DMAEN1); + // register_set(&DAC->CR, (6U << DAC_CR_TSEL1_Pos), DAC_CR_TSEL1); + register_set_bits(&dac->CR, DAC_CR_TEN1); + } else { + register_clear_bits(&dac->CR, DAC_CR_DMAEN1); + } + register_set_bits(&dac->CR, DAC_CR_EN1); + break; + case 2: + if (dma) { + register_set_bits(&dac->CR, DAC_CR_DMAEN2); + } else { + register_clear_bits(&dac->CR, DAC_CR_DMAEN2); + } + register_set_bits(&dac->CR, DAC_CR_EN2); + break; + default: + break; + } +} + +// Set channel 1 value, in mV +void dac_set(DAC_TypeDef *dac, uint8_t channel, uint32_t value) { + uint32_t raw_val = MAX(MIN(value * (1UL << 8U) / 3300U, (1UL << 8U)), 0U); + switch(channel) { + case 1: + register_set(&dac->DHR8R1, raw_val, 0xFFU); + break; + case 2: + register_set(&dac->DHR8R2, raw_val, 0xFFU); + break; + default: + break; + } +} diff --git a/panda/board/stm32h7/llexti.h b/panda/board/stm32h7/llexti.h new file mode 100644 index 0000000..46d0aca --- /dev/null +++ b/panda/board/stm32h7/llexti.h @@ -0,0 +1,63 @@ +void EXTI_IRQ_Handler(void); + +void exti_irq_init(void) { + if (harness.status == HARNESS_STATUS_FLIPPED) { + // CAN2_RX IRQ on falling edge (EXTI10) + current_board->enable_can_transceiver(3U, false); + SYSCFG->EXTICR[2] &= ~(SYSCFG_EXTICR3_EXTI10_Msk); + SYSCFG->EXTICR[2] |= (SYSCFG_EXTICR3_EXTI10_PG); + EXTI->IMR1 |= EXTI_IMR1_IM10; + EXTI->RTSR1 &= ~EXTI_RTSR1_TR10; // rising edge + EXTI->FTSR1 |= EXTI_FTSR1_TR10; // falling edge + + // IRQ on falling edge for PA1 (SBU2, EXTI1) + SYSCFG->EXTICR[0] &= ~(SYSCFG_EXTICR1_EXTI1_Msk); + SYSCFG->EXTICR[0] |= (SYSCFG_EXTICR1_EXTI1_PA); + EXTI->IMR1 |= EXTI_IMR1_IM1; + EXTI->RTSR1 &= ~EXTI_RTSR1_TR1; // rising edge + EXTI->FTSR1 |= EXTI_FTSR1_TR1; // falling edge + REGISTER_INTERRUPT(EXTI1_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI1_IRQn); + REGISTER_INTERRUPT(EXTI15_10_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI15_10_IRQn); + } else { + // CAN0_RX IRQ on falling edge (EXTI8) + current_board->enable_can_transceiver(1U, false); + SYSCFG->EXTICR[2] &= ~(SYSCFG_EXTICR3_EXTI8_Msk); + SYSCFG->EXTICR[2] |= (SYSCFG_EXTICR3_EXTI8_PB); + EXTI->IMR1 |= EXTI_IMR1_IM8; + EXTI->RTSR1 &= ~EXTI_RTSR1_TR8; // rising edge + EXTI->FTSR1 |= EXTI_FTSR1_TR8; // falling edge + + // IRQ on falling edge for PC4 (SBU1, EXTI4) + SYSCFG->EXTICR[1] &= ~(SYSCFG_EXTICR2_EXTI4_Msk); + SYSCFG->EXTICR[1] |= (SYSCFG_EXTICR2_EXTI4_PC); + EXTI->IMR1 |= EXTI_IMR1_IM4; + EXTI->RTSR1 &= ~EXTI_RTSR1_TR4; // rising edge + EXTI->FTSR1 |= EXTI_FTSR1_TR4; // falling edge + REGISTER_INTERRUPT(EXTI4_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI4_IRQn); + REGISTER_INTERRUPT(EXTI9_5_IRQn, EXTI_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_EXTI) + NVIC_EnableIRQ(EXTI9_5_IRQn); + } +} + +bool check_exti_irq(void) { + return ((EXTI->PR1 & EXTI_PR1_PR8) || (EXTI->PR1 & EXTI_PR1_PR10) || (EXTI->PR1 & EXTI_PR1_PR1) || (EXTI->PR1 & EXTI_PR1_PR4)); +} + +void exti_irq_clear(void) { + // Clear pending bits + EXTI->PR1 |= EXTI_PR1_PR8; + EXTI->PR1 |= EXTI_PR1_PR10; + EXTI->PR1 |= EXTI_PR1_PR4; + EXTI->PR1 |= EXTI_PR1_PR1; // works + EXTI->PR1 |= EXTI_PR1_PR19; // works + + // Disable all active EXTI IRQs + EXTI->IMR1 &= ~EXTI_IMR1_IM8; + EXTI->IMR1 &= ~EXTI_IMR1_IM10; + EXTI->IMR1 &= ~EXTI_IMR1_IM4; + EXTI->IMR1 &= ~EXTI_IMR1_IM1; + EXTI->IMR1 &= ~EXTI_IMR1_IM19; +} diff --git a/panda/board/stm32h7/llfan.h b/panda/board/stm32h7/llfan.h new file mode 100644 index 0000000..dce6225 --- /dev/null +++ b/panda/board/stm32h7/llfan.h @@ -0,0 +1,23 @@ +// TACH interrupt handler +void EXTI2_IRQ_Handler(void) { + volatile unsigned int pr = EXTI->PR1 & (1U << 2); + if ((pr & (1U << 2)) != 0U) { + fan_state.tach_counter++; + } + EXTI->PR1 = (1U << 2); +} + +void llfan_init(void) { + // 5000RPM * 4 tach edges / 60 seconds + REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 700U, FAULT_INTERRUPT_RATE_TACH) + + // Init PWM speed control + pwm_init(TIM3, 3); + + // Init TACH interrupt + register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U); + register_set_bits(&(EXTI->IMR1), (1U << 2)); + register_set_bits(&(EXTI->RTSR1), (1U << 2)); + register_set_bits(&(EXTI->FTSR1), (1U << 2)); + NVIC_EnableIRQ(EXTI2_IRQn); +} diff --git a/panda/board/stm32h7/llfdcan.h b/panda/board/stm32h7/llfdcan.h new file mode 100644 index 0000000..4bb6d3d --- /dev/null +++ b/panda/board/stm32h7/llfdcan.h @@ -0,0 +1,269 @@ +// SAE J2284-4 document specifies a bus-line network running at 2 Mbit/s +// SAE J2284-5 document specifies a point-to-point communication running at 5 Mbit/s + +#define CAN_PCLK 80000U // KHz, sourced from PLL1Q +#define BITRATE_PRESCALER 2U // Valid from 250Kbps to 5Mbps with 80Mhz clock +#define CAN_SP_NOMINAL 80U // 80% for both SAE J2284-4 and SAE J2284-5 +#define CAN_SP_DATA_2M 80U // 80% for SAE J2284-4 +#define CAN_SP_DATA_5M 75U // 75% for SAE J2284-5 +#define CAN_QUANTA(speed, prescaler) (CAN_PCLK / ((speed) / 10U * (prescaler))) +#define CAN_SEG1(tq, sp) (((tq) * (sp) / 100U)- 1U) +#define CAN_SEG2(tq, sp) ((tq) * (100U - (sp)) / 100U) + +// FDCAN core settings +#define FDCAN_MESSAGE_RAM_SIZE 0x2800UL +#define FDCAN_START_ADDRESS 0x4000AC00UL +#define FDCAN_OFFSET 3384UL // bytes for each FDCAN module, equally +#define FDCAN_OFFSET_W 846UL // words for each FDCAN module, equally +#define FDCAN_END_ADDRESS 0x4000D3FCUL // Message RAM has a width of 4 bytes + +// FDCAN_RX_FIFO_0_EL_CNT + FDCAN_TX_FIFO_EL_CNT can't exceed 47 elements (47 * 72 bytes = 3,384 bytes) per FDCAN module + +// RX FIFO 0 +#define FDCAN_RX_FIFO_0_EL_CNT 46UL +#define FDCAN_RX_FIFO_0_HEAD_SIZE 8UL // bytes +#define FDCAN_RX_FIFO_0_DATA_SIZE 64UL // bytes +#define FDCAN_RX_FIFO_0_EL_SIZE (FDCAN_RX_FIFO_0_HEAD_SIZE + FDCAN_RX_FIFO_0_DATA_SIZE) +#define FDCAN_RX_FIFO_0_EL_W_SIZE (FDCAN_RX_FIFO_0_EL_SIZE / 4UL) +#define FDCAN_RX_FIFO_0_OFFSET 0UL + +// TX FIFO +#define FDCAN_TX_FIFO_EL_CNT 1UL +#define FDCAN_TX_FIFO_HEAD_SIZE 8UL // bytes +#define FDCAN_TX_FIFO_DATA_SIZE 64UL // bytes +#define FDCAN_TX_FIFO_EL_SIZE (FDCAN_TX_FIFO_HEAD_SIZE + FDCAN_TX_FIFO_DATA_SIZE) +#define FDCAN_TX_FIFO_EL_W_SIZE (FDCAN_TX_FIFO_EL_SIZE / 4UL) +#define FDCAN_TX_FIFO_OFFSET (FDCAN_RX_FIFO_0_OFFSET + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_W_SIZE)) + +#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? "FDCAN1" : (((CAN_DEV) == FDCAN2) ? "FDCAN2" : "FDCAN3")) +#define CAN_NUM_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? 0UL : (((CAN_DEV) == FDCAN2) ? 1UL : 2UL)) + + +void print(const char *a); + +// kbps multiplied by 10 +const uint32_t speeds[] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U}; +const uint32_t data_speeds[] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U, 20000U, 50000U}; + + +bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) { + bool ret = true; + // Exit from sleep mode + FDCANx->CCCR &= ~(FDCAN_CCCR_CSR); + while ((FDCANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA); + + // Request init + uint32_t timeout_counter = 0U; + FDCANx->CCCR |= FDCAN_CCCR_INIT; + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) == 0) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if (timeout_counter >= CAN_INIT_TIMEOUT_MS){ + ret = false; + break; + } + } + return ret; +} + +bool fdcan_exit_init(FDCAN_GlobalTypeDef *FDCANx) { + bool ret = true; + + FDCANx->CCCR &= ~(FDCAN_CCCR_INIT); + uint32_t timeout_counter = 0U; + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) != 0) { + // Delay for about 1ms + delay(10000); + timeout_counter++; + + if (timeout_counter >= CAN_INIT_TIMEOUT_MS) { + ret = false; + break; + } + } + return ret; +} + +bool llcan_set_speed(FDCAN_GlobalTypeDef *FDCANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent) { + UNUSED(speed); + bool ret = fdcan_request_init(FDCANx); + + if (ret) { + // Enable config change + FDCANx->CCCR |= FDCAN_CCCR_CCE; + + //Reset operation mode to Normal + FDCANx->CCCR &= ~(FDCAN_CCCR_TEST); + FDCANx->TEST &= ~(FDCAN_TEST_LBCK); + FDCANx->CCCR &= ~(FDCAN_CCCR_MON); + FDCANx->CCCR &= ~(FDCAN_CCCR_ASM); + FDCANx->CCCR &= ~(FDCAN_CCCR_NISO); + + // TODO: add as a separate safety mode + // Enable ASM restricted operation(for debug or automatic bitrate switching) + //FDCANx->CCCR |= FDCAN_CCCR_ASM; + + uint8_t prescaler = BITRATE_PRESCALER; + if (speed < 2500U) { + // The only way to support speeds lower than 250Kbit/s (down to 10Kbit/s) + prescaler = BITRATE_PRESCALER * 16U; + } + + // Set the nominal bit timing values + uint32_t tq = CAN_QUANTA(speed, prescaler); + uint32_t sp = CAN_SP_NOMINAL; + uint32_t seg1 = CAN_SEG1(tq, sp); + uint32_t seg2 = CAN_SEG2(tq, sp); + uint8_t sjw = MIN(127U, seg2); + + FDCANx->NBTP = (((sjw & 0x7FU)-1U)<DBTP = (((sjw & 0xFU)-1U)<CCCR |= FDCAN_CCCR_NISO; + } + + // Silent loopback is known as internal loopback in the docs + if (loopback) { + FDCANx->CCCR |= FDCAN_CCCR_TEST; + FDCANx->TEST |= FDCAN_TEST_LBCK; + FDCANx->CCCR |= FDCAN_CCCR_MON; + } + // Silent is known as bus monitoring in the docs + if (silent) { + FDCANx->CCCR |= FDCAN_CCCR_MON; + } + ret = fdcan_exit_init(FDCANx); + if (!ret) { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (2)\n"); + } + } else { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (1)\n"); + } + return ret; +} + +void llcan_irq_disable(const FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { + NVIC_DisableIRQ(FDCAN1_IT0_IRQn); + NVIC_DisableIRQ(FDCAN1_IT1_IRQn); + } else if (FDCANx == FDCAN2) { + NVIC_DisableIRQ(FDCAN2_IT0_IRQn); + NVIC_DisableIRQ(FDCAN2_IT1_IRQn); + } else if (FDCANx == FDCAN3) { + NVIC_DisableIRQ(FDCAN3_IT0_IRQn); + NVIC_DisableIRQ(FDCAN3_IT1_IRQn); + } else { + } +} + +void llcan_irq_enable(const FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + } else if (FDCANx == FDCAN2) { + NVIC_EnableIRQ(FDCAN2_IT0_IRQn); + NVIC_EnableIRQ(FDCAN2_IT1_IRQn); + } else if (FDCANx == FDCAN3) { + NVIC_EnableIRQ(FDCAN3_IT0_IRQn); + NVIC_EnableIRQ(FDCAN3_IT1_IRQn); + } else { + } +} + +bool llcan_init(FDCAN_GlobalTypeDef *FDCANx) { + uint32_t can_number = CAN_NUM_FROM_CANIF(FDCANx); + bool ret = fdcan_request_init(FDCANx); + + if (ret) { + // Enable config change + FDCANx->CCCR |= FDCAN_CCCR_CCE; + // Enable automatic retransmission + FDCANx->CCCR &= ~(FDCAN_CCCR_DAR); + // Enable transmission pause feature + FDCANx->CCCR |= FDCAN_CCCR_TXP; + // Disable protocol exception handling + FDCANx->CCCR |= FDCAN_CCCR_PXHD; + // FD with BRS + FDCANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + + // Set TX mode to FIFO + FDCANx->TXBC &= ~(FDCAN_TXBC_TFQM); + // Configure TX element data size + FDCANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes + //Configure RX FIFO0 element data size + FDCANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos; + // Disable filtering, accept all valid frames received + FDCANx->XIDFC &= ~(FDCAN_XIDFC_LSE); // No extended filters + FDCANx->SIDFC &= ~(FDCAN_SIDFC_LSS); // No standard filters + FDCANx->GFC &= ~(FDCAN_GFC_RRFE); // Accept extended remote frames + FDCANx->GFC &= ~(FDCAN_GFC_RRFS); // Accept standard remote frames + FDCANx->GFC &= ~(FDCAN_GFC_ANFE); // Accept extended frames to FIFO 0 + FDCANx->GFC &= ~(FDCAN_GFC_ANFS); // Accept standard frames to FIFO 0 + + uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); + uint32_t TxFIFOSA = RxFIFO0SA + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); + + // RX FIFO 0 + FDCANx->RXF0C |= (FDCAN_RX_FIFO_0_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_RXF0C_F0SA_Pos; + FDCANx->RXF0C |= FDCAN_RX_FIFO_0_EL_CNT << FDCAN_RXF0C_F0S_Pos; + // RX FIFO 0 switch to non-blocking (overwrite) mode + FDCANx->RXF0C |= FDCAN_RXF0C_F0OM; + + // TX FIFO (mode set earlier) + FDCANx->TXBC |= (FDCAN_TX_FIFO_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_TXBC_TBSA_Pos; + FDCANx->TXBC |= FDCAN_TX_FIFO_EL_CNT << FDCAN_TXBC_TFQS_Pos; + + // Flush allocated RAM + uint32_t EndAddress = TxFIFOSA + (FDCAN_TX_FIFO_EL_CNT * FDCAN_TX_FIFO_EL_SIZE); + for (uint32_t RAMcounter = RxFIFO0SA; RAMcounter < EndAddress; RAMcounter += 4U) { + *(uint32_t *)(RAMcounter) = 0x00000000; + } + + // Enable both interrupts for each module + FDCANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1); + + FDCANx->IE &= 0x0U; // Reset all interrupts + // Messages for INT0 + FDCANx->IE |= FDCAN_IE_RF0NE; // Rx FIFO 0 new message + FDCANx->IE |= FDCAN_IE_PEDE | FDCAN_IE_PEAE | FDCAN_IE_BOE | FDCAN_IE_EPE | FDCAN_IE_RF0LE; + + // Messages for INT1 (Only TFE works??) + FDCANx->ILS |= FDCAN_ILS_TFEL; + FDCANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty + + ret = fdcan_exit_init(FDCANx); + if(!ret) { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (2)!\n"); + } + + llcan_irq_enable(FDCANx); + + } else { + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (1)!\n"); + } + return ret; +} + +void llcan_clear_send(FDCAN_GlobalTypeDef *FDCANx) { + // from datasheet: "Transmit cancellation is not intended for Tx FIFO operation." + // so we need to clear pending transmission manually by resetting FDCAN core + FDCANx->IR |= 0x3FCFFFFFU; // clear all interrupts + bool ret = llcan_init(FDCANx); + UNUSED(ret); +} diff --git a/panda/board/stm32h7/llflash.h b/panda/board/stm32h7/llflash.h new file mode 100644 index 0000000..b95011a --- /dev/null +++ b/panda/board/stm32h7/llflash.h @@ -0,0 +1,33 @@ +bool flash_is_locked(void) { + return (FLASH->CR1 & FLASH_CR_LOCK); +} + +void flash_unlock(void) { + FLASH->KEYR1 = 0x45670123; + FLASH->KEYR1 = 0xCDEF89AB; +} + +bool flash_erase_sector(uint8_t sector, bool unlocked) { + // don't erase the bootloader(sector 0) + if (sector != 0 && sector < 8 && unlocked) { + FLASH->CR1 = (sector << 8) | FLASH_CR_SER; + FLASH->CR1 |= FLASH_CR_START; + while (FLASH->SR1 & FLASH_SR_QW); + return true; + } + return false; +} + +void flash_write_word(void *prog_ptr, uint32_t data) { + uint32_t *pp = prog_ptr; + FLASH->CR1 |= FLASH_CR_PG; + *pp = data; + while (FLASH->SR1 & FLASH_SR_QW); +} + +void flush_write_buffer(void) { + if (FLASH->SR1 & FLASH_SR_WBNE) { + FLASH->CR1 |= FLASH_CR_FW; + while (FLASH->SR1 & FLASH_CR_FW); + } +} diff --git a/panda/board/stm32h7/lli2c.h b/panda/board/stm32h7/lli2c.h new file mode 100644 index 0000000..1dd4b4c --- /dev/null +++ b/panda/board/stm32h7/lli2c.h @@ -0,0 +1,153 @@ + +// TODO: this driver relies heavily on polling, +// if we want it to be more async, we should use interrupts + +#define I2C_TIMEOUT_US 100000U + +// cppcheck-suppress misra-c2012-2.7; not sure why it triggers here? +bool i2c_status_wait(const volatile uint32_t *reg, uint32_t mask, uint32_t val) { + uint32_t start_time = microsecond_timer_get(); + while(((*reg & mask) != val) && (get_ts_elapsed(microsecond_timer_get(), start_time) < I2C_TIMEOUT_US)); + return ((*reg & mask) == val); +} + +bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value) { + // Setup transfer and send START + addr + bool ret = false; + for(uint32_t i=0U; i<10U; i++) { + register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); + I2C->CR2 = ((addr << 1U) & I2C_CR2_SADD_Msk); + register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); + register_set_bits(&I2C->CR2, I2C_CR2_AUTOEND); + I2C->CR2 |= (2 << I2C_CR2_NBYTES_Pos); + + I2C->CR2 |= I2C_CR2_START; + if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { + continue; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + } else { + ret = true; + break; + } + } + + if (!ret) { + goto end; + } + + // Send data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = reg; + + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = value; + +end: + return ret; +} + +bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value) { + // Setup transfer and send START + addr + bool ret = false; + for(uint32_t i=0U; i<10U; i++) { + register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); + I2C->CR2 = ((addr << 1U) & I2C_CR2_SADD_Msk); + register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); + register_clear_bits(&I2C->CR2, I2C_CR2_AUTOEND); + I2C->CR2 |= (1 << I2C_CR2_NBYTES_Pos); + + I2C->CR2 |= I2C_CR2_START; + if(!i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U)) { + continue; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + } else { + ret = true; + break; + } + } + + if (!ret) { + goto end; + } + + // Send data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_TXIS, I2C_ISR_TXIS); + if(!ret) { + goto end; + } + I2C->TXDR = reg; + + // Restart + I2C->CR2 = (((addr << 1) | 0x1U) & I2C_CR2_SADD_Msk) | (1U << I2C_CR2_NBYTES_Pos) | I2C_CR2_RD_WRN | I2C_CR2_START; + ret = i2c_status_wait(&I2C->CR2, I2C_CR2_START, 0U); + if(!ret) { + goto end; + } + + // check if we lost arbitration + if ((I2C->ISR & I2C_ISR_ARLO) != 0U) { + register_set_bits(&I2C->ICR, I2C_ICR_ARLOCF); + ret = false; + goto end; + } + + // Read data + ret = i2c_status_wait(&I2C->ISR, I2C_ISR_RXNE, I2C_ISR_RXNE); + if(!ret) { + goto end; + } + *value = I2C->RXDR; + + // Stop + I2C->CR2 |= I2C_CR2_STOP; + +end: + return ret; +} + +bool i2c_set_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { + uint8_t value; + bool ret = i2c_read_reg(I2C, address, regis, &value); + if(ret) { + ret = i2c_write_reg(I2C, address, regis, value | bits); + } + return ret; +} + +bool i2c_clear_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { + uint8_t value; + bool ret = i2c_read_reg(I2C, address, regis, &value); + if(ret) { + ret = i2c_write_reg(I2C, address, regis, value & (uint8_t) (~bits)); + } + return ret; +} + +bool i2c_set_reg_mask(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t value, uint8_t mask) { + uint8_t old_value; + bool ret = i2c_read_reg(I2C, address, regis, &old_value); + if(ret) { + ret = i2c_write_reg(I2C, address, regis, (old_value & (uint8_t) (~mask)) | (value & mask)); + } + return ret; +} + +void i2c_init(I2C_TypeDef *I2C) { + // 100kHz clock speed + I2C->TIMINGR = 0x107075B0; + I2C->CR1 = I2C_CR1_PE; +} \ No newline at end of file diff --git a/panda/board/stm32h7/llspi.h b/panda/board/stm32h7/llspi.h new file mode 100644 index 0000000..1947803 --- /dev/null +++ b/panda/board/stm32h7/llspi.h @@ -0,0 +1,106 @@ +// 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; + } + + // clear all pending + SPI4->IFCR |= (0x1FFU << 3U); + register_set(&(SPI4->IER), 0, 0x3FFU); + + // 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 + SPI + DMA2_Stream3->CR &= ~DMA_SxCR_EN; + register_clear_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE); + + // 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 |= (0x1FFU << 3U); + + // setup interrupt on TXC + register_set(&(SPI4->IER), (1U << SPI_IER_EOTIE_Pos), 0x3FFU); + + // enable DMA + SPI + register_set_bits(&(SPI4->CFG1), SPI_CFG1_TXDMAEN); + DMA2_Stream3->CR |= DMA_SxCR_EN; + register_set_bits(&(SPI4->CR1), SPI_CR1_SPE); +} + +// master -> panda DMA finished +void DMA2_Stream2_IRQ_Handler(void) { + // Clear interrupt flag + DMA2->LIFCR = DMA_LIFCR_CTCIF2; + + spi_rx_done(); +} + +// panda -> master DMA finished +void DMA2_Stream3_IRQ_Handler(void) { + ENTER_CRITICAL(); + + DMA2->LIFCR = DMA_LIFCR_CTCIF3; + spi_tx_dma_done = true; + + EXIT_CRITICAL(); +} + +// panda TX finished +void SPI4_IRQ_Handler(void) { + // clear flag + SPI4->IFCR |= (0x1FFU << 3U); + + if (spi_tx_dma_done && ((SPI4->SR & SPI_SR_TXC) != 0)) { + spi_tx_dma_done = false; + spi_tx_done(false); + } +} + + +void llspi_init(void) { + REGISTER_INTERRUPT(SPI4_IRQn, SPI4_IRQ_Handler, (SPI_IRQ_RATE * 2U), FAULT_INTERRUPT_RATE_SPI) + REGISTER_INTERRUPT(DMA2_Stream2_IRQn, DMA2_Stream2_IRQ_Handler, SPI_IRQ_RATE, FAULT_INTERRUPT_RATE_SPI_DMA) + REGISTER_INTERRUPT(DMA2_Stream3_IRQn, DMA2_Stream3_IRQ_Handler, SPI_IRQ_RATE, 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->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->CR1), SPI_CR1_SPE, 0xFFFFU); + register_set(&(SPI4->CR2), 0, 0xFFFFU); + + NVIC_EnableIRQ(DMA2_Stream2_IRQn); + NVIC_EnableIRQ(DMA2_Stream3_IRQn); + NVIC_EnableIRQ(SPI4_IRQn); +} diff --git a/panda/board/stm32h7/lluart.h b/panda/board/stm32h7/lluart.h new file mode 100644 index 0000000..0ad7b6a --- /dev/null +++ b/panda/board/stm32h7/lluart.h @@ -0,0 +1,106 @@ +void uart_rx_ring(uart_ring *q){ + // Do not read out directly if DMA enabled + ENTER_CRITICAL(); + + // Read out RX buffer + uint8_t c = q->uart->RDR; // This read after reading SR clears a bunch of interrupts + + uint16_t next_w_ptr = (q->w_ptr_rx + 1U) % q->rx_fifo_size; + + if ((next_w_ptr == q->r_ptr_rx) && q->overwrite) { + // overwrite mode: drop oldest byte + q->r_ptr_rx = (q->r_ptr_rx + 1U) % q->rx_fifo_size; + } + + // Do not overwrite buffer data + if (next_w_ptr != q->r_ptr_rx) { + q->elems_rx[q->w_ptr_rx] = c; + q->w_ptr_rx = next_w_ptr; + if (q->callback != NULL) { + q->callback(q); + } + } + + EXIT_CRITICAL(); +} + +void uart_tx_ring(uart_ring *q){ + ENTER_CRITICAL(); + // Send out next byte of TX buffer + if (q->w_ptr_tx != q->r_ptr_tx) { + // Only send if transmit register is empty (aka last byte has been sent) + if ((q->uart->ISR & USART_ISR_TXE_TXFNF) != 0) { + q->uart->TDR = q->elems_tx[q->r_ptr_tx]; // This clears TXE + q->r_ptr_tx = (q->r_ptr_tx + 1U) % q->tx_fifo_size; + } + + // Enable TXE interrupt if there is still data to be sent + if(q->r_ptr_tx != q->w_ptr_tx){ + q->uart->CR1 |= USART_CR1_TXEIE; + } else { + q->uart->CR1 &= ~USART_CR1_TXEIE; + } + } + EXIT_CRITICAL(); +} + +void uart_set_baud(USART_TypeDef *u, unsigned int baud) { + // UART7 is connected to APB1 at 60MHz + u->BRR = 60000000U / baud; +} + +// This read after reading ISR clears all error interrupts. We don't want compiler warnings, nor optimizations +#define UART_READ_RDR(uart) volatile uint8_t t = (uart)->RDR; UNUSED(t); + +void uart_interrupt_handler(uart_ring *q) { + ENTER_CRITICAL(); + + // Read UART status. This is also the first step necessary in clearing most interrupts + uint32_t status = q->uart->ISR; + + // If RXFNE is set, perform a read. This clears RXFNE, ORE, IDLE, NF and FE + if((status & USART_ISR_RXNE_RXFNE) != 0U){ + uart_rx_ring(q); + } + + // Detect errors and clear them + uint32_t err = (status & USART_ISR_ORE) | (status & USART_ISR_NE) | (status & USART_ISR_FE) | (status & USART_ISR_PE); + if(err != 0U){ + #ifdef DEBUG_UART + print("Encountered UART error: "); puth(err); print("\n"); + #endif + UART_READ_RDR(q->uart) + } + + if ((err & USART_ISR_ORE) != 0U) { + q->uart->ICR |= USART_ICR_ORECF; + } else if ((err & USART_ISR_NE) != 0U) { + q->uart->ICR |= USART_ICR_NECF; + } else if ((err & USART_ISR_FE) != 0U) { + q->uart->ICR |= USART_ICR_FECF; + } else if ((err & USART_ISR_PE) != 0U) { + q->uart->ICR |= USART_ICR_PECF; + } else {} + + // Send if necessary + uart_tx_ring(q); + + EXIT_CRITICAL(); +} + +void UART7_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_som_debug); } + +void uart_init(uart_ring *q, int baud) { + if (q->uart == UART7) { + REGISTER_INTERRUPT(UART7_IRQn, UART7_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_7) + + uart_set_baud(q->uart, baud); + q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; + + // Enable interrupt on RX not empty + q->uart->CR1 |= USART_CR1_RXNEIE; + + // Enable UART interrupts + NVIC_EnableIRQ(UART7_IRQn); + } +} diff --git a/panda/board/stm32h7/llusb.h b/panda/board/stm32h7/llusb.h new file mode 100644 index 0000000..ada1630 --- /dev/null +++ b/panda/board/stm32h7/llusb.h @@ -0,0 +1,91 @@ +USB_OTG_GlobalTypeDef *USBx = USB_OTG_HS; + +#define USBx_HOST ((USB_OTG_HostTypeDef *)((uint32_t)USBx + USB_OTG_HOST_BASE)) +#define USBx_DEVICE ((USB_OTG_DeviceTypeDef *)((uint32_t)USBx + USB_OTG_DEVICE_BASE)) +#define USBx_INEP(i) ((USB_OTG_INEndpointTypeDef *)((uint32_t)USBx + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USBx + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE))) +#define USBx_DFIFO(i) *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE)) +#define USBx_PCGCCTL *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_PCGCCTL_BASE) + +#define USBD_FS_TRDT_VALUE 6UL +#define USB_OTG_SPEED_FULL 3U +#define DCFG_FRAME_INTERVAL_80 0U + + +void usb_irqhandler(void); + +void OTG_HS_IRQ_Handler(void) { + NVIC_DisableIRQ(OTG_HS_IRQn); + usb_irqhandler(); + NVIC_EnableIRQ(OTG_HS_IRQn); +} + +void usb_init(void) { + REGISTER_INTERRUPT(OTG_HS_IRQn, OTG_HS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) // TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate + + // Disable global interrupt + USBx->GAHBCFG &= ~(USB_OTG_GAHBCFG_GINT); + // Select FS Embedded PHY + USBx->GUSBCFG |= USB_OTG_GUSBCFG_PHYSEL; + // Force device mode + USBx->GUSBCFG &= ~(USB_OTG_GUSBCFG_FHMOD | USB_OTG_GUSBCFG_FDMOD); + USBx->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; + delay(250000); // Wait for about 25ms (explicitly stated in H7 ref manual) + // Wait for AHB master IDLE state. + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0); + // Core Soft Reset + USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST); + // Activate the USB Transceiver + USBx->GCCFG |= USB_OTG_GCCFG_PWRDWN; + + for (uint8_t i = 0U; i < 15U; i++) { + USBx->DIEPTXF[i] = 0U; + } + + // VBUS Sensing setup + USBx_DEVICE->DCTL |= USB_OTG_DCTL_SDIS; + // Deactivate VBUS Sensing B + USBx->GCCFG &= ~(USB_OTG_GCCFG_VBDEN); + // B-peripheral session valid override enable + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN; + USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; + // Restart the Phy Clock + USBx_PCGCCTL = 0U; + // Device mode configuration + USBx_DEVICE->DCFG |= DCFG_FRAME_INTERVAL_80; + USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK; + + // Flush FIFOs + USBx->GRSTCTL = (USB_OTG_GRSTCTL_TXFFLSH | (0x10U << 6)); + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_TXFFLSH) == USB_OTG_GRSTCTL_TXFFLSH); + + USBx->GRSTCTL = USB_OTG_GRSTCTL_RXFFLSH; + while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_RXFFLSH) == USB_OTG_GRSTCTL_RXFFLSH); + + // Clear all pending Device Interrupts + USBx_DEVICE->DIEPMSK = 0U; + USBx_DEVICE->DOEPMSK = 0U; + USBx_DEVICE->DAINTMSK = 0U; + USBx_DEVICE->DIEPMSK &= ~(USB_OTG_DIEPMSK_TXFURM); + + // Disable all interrupts. + USBx->GINTMSK = 0U; + // Clear any pending interrupts + USBx->GINTSTS = 0xBFFFFFFFU; + // Enable 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 | + USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM; + + // 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 + USBx->GAHBCFG |= USB_OTG_GAHBCFG_GINT; + // Soft disconnect disable: + USBx_DEVICE->DCTL &= ~(USB_OTG_DCTL_SDIS); + + // enable the IRQ + NVIC_EnableIRQ(OTG_HS_IRQn); +} diff --git a/panda/board/stm32h7/peripherals.h b/panda/board/stm32h7/peripherals.h new file mode 100644 index 0000000..b60f190 --- /dev/null +++ b/panda/board/stm32h7/peripherals.h @@ -0,0 +1,138 @@ +void gpio_usb_init(void) { + // A11,A12: USB + set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG1_FS); + set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG1_FS); + GPIOA->OSPEEDR = GPIO_OSPEEDR_OSPEED11 | GPIO_OSPEEDR_OSPEED12; +} + +void gpio_spi_init(void) { + 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); +} + +void gpio_usart2_init(void) { + // A2,A3: USART 2 for debugging + set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2); + set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2); +} + +void gpio_uart7_init(void) { + // E7,E8: UART 7 for debugging + set_gpio_alternate(GPIOE, 7, GPIO_AF7_UART7); + set_gpio_alternate(GPIOE, 8, GPIO_AF7_UART7); +} + +// Common GPIO initialization +void common_init_gpio(void) { + /// E2,E3,E4: RGB LED + set_gpio_pullup(GPIOE, 2, PULL_NONE); + set_gpio_mode(GPIOE, 2, MODE_OUTPUT); + set_gpio_output_type(GPIOE, 2, OUTPUT_TYPE_OPEN_DRAIN); + + set_gpio_pullup(GPIOE, 3, PULL_NONE); + set_gpio_mode(GPIOE, 3, MODE_OUTPUT); + set_gpio_output_type(GPIOE, 3, OUTPUT_TYPE_OPEN_DRAIN); + + set_gpio_pullup(GPIOE, 4, PULL_NONE); + set_gpio_mode(GPIOE, 4, MODE_OUTPUT); + set_gpio_output_type(GPIOE, 4, OUTPUT_TYPE_OPEN_DRAIN); + + //C4,A1: OBD_SBU1, OBD_SBU2 + set_gpio_pullup(GPIOC, 4, PULL_NONE); + set_gpio_mode(GPIOC, 4, MODE_ANALOG); + + set_gpio_pullup(GPIOA, 1, PULL_NONE); + set_gpio_mode(GPIOA, 1, MODE_ANALOG); + + //F11: VOLT_S + set_gpio_pullup(GPIOF, 11, PULL_NONE); + set_gpio_mode(GPIOF, 11, MODE_ANALOG); + + gpio_usb_init(); + + // B8,B9: FDCAN1 + set_gpio_pullup(GPIOB, 8, PULL_NONE); + set_gpio_alternate(GPIOB, 8, GPIO_AF9_FDCAN1); + + set_gpio_pullup(GPIOB, 9, PULL_NONE); + set_gpio_alternate(GPIOB, 9, GPIO_AF9_FDCAN1); + + // B5,B6 (mplex to B12,B13): FDCAN2 + set_gpio_pullup(GPIOB, 12, PULL_NONE); + set_gpio_pullup(GPIOB, 13, PULL_NONE); + + set_gpio_pullup(GPIOB, 5, PULL_NONE); + set_gpio_alternate(GPIOB, 5, GPIO_AF9_FDCAN2); + + set_gpio_pullup(GPIOB, 6, PULL_NONE); + set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + + // G9,G10: FDCAN3 + set_gpio_pullup(GPIOG, 9, PULL_NONE); + set_gpio_alternate(GPIOG, 9, GPIO_AF2_FDCAN3); + + set_gpio_pullup(GPIOG, 10, PULL_NONE); + set_gpio_alternate(GPIOG, 10, GPIO_AF2_FDCAN3); +} + +void flasher_peripherals_init(void) { + RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; + + // SPI + DMA + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; +} + +// Peripheral initialization +void peripherals_init(void) { + // enable GPIO(A,B,C,D,E,F,G,H) + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOBEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOCEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIODEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOEEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; + RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; + + // Enable CPU access to SRAM1 and SRAM2 (in domain D2) for DMA + RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN; + + // Supplemental + RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DAC DMA + RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // SPI DMA + RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; + + // Connectivity + RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI + RCC->APB1LENR |= RCC_APB1LENR_I2C5EN; // codec I2C + RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; // USB + RCC->AHB1LPENR |= RCC_AHB1LPENR_USB1OTGHSLPEN; // USB LP needed for CSleep state(__WFI()) + RCC->AHB1LPENR &= ~(RCC_AHB1LPENR_USB1OTGHSULPILPEN); // disable USB ULPI + RCC->APB1LENR |= RCC_APB1LENR_UART7EN; // SOM uart + RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; // FDCAN core enable + + // Analog + RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC12 clocks + RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC + + // Timers + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer + RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter + RCC->APB1LENR |= RCC_APB1LENR_TIM3EN; // fan pwm + RCC->APB1LENR |= RCC_APB1LENR_TIM6EN; // interrupt timer + RCC->APB1LENR |= RCC_APB1LENR_TIM7EN; // DMA trigger timer + RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // tick timer + RCC->APB1LENR |= RCC_APB1LENR_TIM12EN; // slow loop + +#ifdef PANDA_JUNGLE + RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC + RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks +#endif +} + +void enable_interrupt_timer(void) { + register_set_bits(&(RCC->APB1LENR), RCC_APB1LENR_TIM6EN); // Enable interrupt timer peripheral +} diff --git a/panda/board/stm32h7/stm32h7_config.h b/panda/board/stm32h7/stm32h7_config.h new file mode 100644 index 0000000..6f36a5a --- /dev/null +++ b/panda/board/stm32h7/stm32h7_config.h @@ -0,0 +1,99 @@ +#include "stm32h7/inc/stm32h7xx.h" +#include "stm32h7/inc/stm32h7xx_hal_gpio_ex.h" +#define MCU_IDCODE 0x483U + +// from the linker script +#define APP_START_ADDRESS 0x8020000U + +#define CORE_FREQ 240U // in Mhz +//APB1 - 120Mhz, APB2 - 120Mhz +#define APB1_FREQ (CORE_FREQ/4U) +#define APB1_TIMER_FREQ (APB1_FREQ*2U) // APB1 is multiplied by 2 for the timer peripherals +#define APB2_FREQ (CORE_FREQ/4U) +#define APB2_TIMER_FREQ (APB2_FREQ*2U) // APB2 is multiplied by 2 for the timer peripherals + +#define BOOTLOADER_ADDRESS 0x1FF09804U + +/* +An IRQ is received on message RX/TX (or RX errors), with +separate IRQs for RX and TX. + +0-byte CAN FD frame as the worst case: +- 17 slow bits = SOF + 11 ID + R1 + IDE + EDL + R0 + BRS +- 23 fast bits = ESI + 4 DLC + 0 DATA + 17 CRC + CRC delimeter +- 12 slow bits = ACK + DEL + 7 EOF + 3 IFS +- all currently supported cars are 0.5 Mbps / 2 Mbps + +1 / ((29 bits / 0.5Mbps) + (23 bits / 2Mbps)) = 14388Hz +*/ +#define CAN_INTERRUPT_RATE 16000U + +#define MAX_LED_FADE 10240U + +// There are 163 external interrupt sources (see stm32f735xx.h) +#define NUM_INTERRUPTS 163U + +#define TICK_TIMER_IRQ TIM8_BRK_TIM12_IRQn +#define TICK_TIMER TIM12 + +#define MICROSECOND_TIMER TIM2 + +#define INTERRUPT_TIMER_IRQ TIM6_DAC_IRQn +#define INTERRUPT_TIMER TIM6 + +#define IND_WDG IWDG1 + +#define PROVISION_CHUNK_ADDRESS 0x080FFFE0U +#define DEVICE_SERIAL_NUMBER_ADDRESS 0x080FFFC0U + +#include "can_definitions.h" +#include "comms_definitions.h" + +#ifndef BOOTSTUB + #include "main_declarations.h" +#else + #include "bootstub_declarations.h" +#endif + +#include "libc.h" +#include "critical.h" +#include "faults.h" +#include "utils.h" + +#include "drivers/registers.h" +#include "drivers/interrupts.h" +#include "drivers/gpio.h" +#include "stm32h7/peripherals.h" +#include "stm32h7/interrupt_handlers.h" +#include "drivers/timers.h" +#include "drivers/watchdog.h" + +#if !defined(BOOTSTUB) + #include "drivers/uart.h" + #include "stm32h7/lluart.h" +#endif + +#include "stm32h7/board.h" +#include "stm32h7/clock.h" + +#if !defined(BOOTSTUB) && defined(PANDA) + #include "stm32h7/llexti.h" +#endif + +#ifdef BOOTSTUB + #include "stm32h7/llflash.h" +#else + #include "stm32h7/llfdcan.h" +#endif + +#include "stm32h7/llusb.h" + +#include "drivers/spi.h" +#include "stm32h7/llspi.h" + +void early_gpio_float(void) { + RCC->AHB4ENR = RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN | RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN | RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN; + GPIOA->MODER = 0xAB000000U; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; + GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0; GPIOD->ODR = 0; GPIOE->ODR = 0; GPIOF->ODR = 0; GPIOG->ODR = 0; GPIOH->ODR = 0; + GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0; GPIOD->PUPDR = 0; GPIOE->PUPDR = 0; GPIOF->PUPDR = 0; GPIOG->PUPDR = 0; GPIOH->PUPDR = 0; +} diff --git a/panda/board/utils.h b/panda/board/utils.h new file mode 100644 index 0000000..fd69db1 --- /dev/null +++ b/panda/board/utils.h @@ -0,0 +1,43 @@ +#define MIN(a, b) ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + (_a < _b) ? _a : _b; \ +}) + +#define MAX(a, b) ({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + (_a > _b) ? _a : _b; \ +}) + +#define CLAMP(x, low, high) ({ \ + __typeof__(x) __x = (x); \ + __typeof__(low) __low = (low);\ + __typeof__(high) __high = (high);\ + (__x > __high) ? __high : ((__x < __low) ? __low : __x); \ +}) + +#define ABS(a) ({ \ + __typeof__ (a) _a = (a); \ + (_a > 0) ? _a : (-_a); \ +}) + +#ifndef NULL +// this just provides a standard implementation of NULL +// in lieu of including libc in the panda build +// cppcheck-suppress [misra-c2012-21.1] +#define NULL ((void*)0) +#endif + +// STM32 HAL defines this +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) +#endif + +#define COMPILE_TIME_ASSERT(pred) ((void)sizeof(char[1 - (2 * (!(pred) ? 1 : 0))])) + +// compute the time elapsed (in microseconds) from 2 counter samples +// case where ts < ts_last is ok: overflow is properly re-casted into uint32_t +uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { + return ts - ts_last; +} diff --git a/panda/crypto/hash-internal.h b/panda/crypto/hash-internal.h new file mode 100644 index 0000000..05ec3ec --- /dev/null +++ b/panda/crypto/hash-internal.h @@ -0,0 +1,63 @@ +/* + * Copyright 2007 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ + +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct HASH_CTX; // forward decl + +typedef struct HASH_VTAB { + void (* const init)(struct HASH_CTX*); + void (* const update)(struct HASH_CTX*, const void*, int); + const uint8_t* (* const final)(struct HASH_CTX*); + const uint8_t* (* const hash)(const void*, int, uint8_t*); + int size; +} HASH_VTAB; + +typedef struct HASH_CTX { + const HASH_VTAB * f; + uint64_t count; + uint8_t buf[64]; + uint32_t state[8]; // upto SHA2 +} HASH_CTX; + +#define HASH_init(ctx) (ctx)->f->init(ctx) +#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len) +#define HASH_final(ctx) (ctx)->f->final(ctx) +#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest) +#define HASH_size(ctx) (ctx)->f->size + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_ diff --git a/panda/crypto/rsa.h b/panda/crypto/rsa.h new file mode 100644 index 0000000..4ba4e10 --- /dev/null +++ b/panda/crypto/rsa.h @@ -0,0 +1,58 @@ +/* rsa.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RSANUMBYTES 128 /* 1024 bit key length */ +#define RSANUMWORDS (RSANUMBYTES / sizeof(uint32_t)) + +typedef struct RSAPublicKey { + int len; /* Length of n[] in number of uint32_t */ + uint32_t n0inv; /* -1 / n[0] mod 2^32 */ + uint32_t n[RSANUMWORDS]; /* modulus as little endian array */ + uint32_t rr[RSANUMWORDS]; /* R^2 as little endian array */ + int exponent; /* 3 or 65537 */ +} RSAPublicKey; + +int RSA_verify(const RSAPublicKey *key, + const uint8_t* signature, + const int len, + const uint8_t* hash, + const int hash_len); + +#ifdef __cplusplus +} +#endif + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_RSA_H_ diff --git a/panda/crypto/sha.h b/panda/crypto/sha.h new file mode 100644 index 0000000..4b51a53 --- /dev/null +++ b/panda/crypto/sha.h @@ -0,0 +1,51 @@ +/* + * Copyright 2005 The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ +#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ + +#include "hash-internal.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef HASH_CTX SHA_CTX; + +void SHA_init(SHA_CTX* ctx); +void SHA_update(SHA_CTX* ctx, const void* data, int len); +const uint8_t* SHA_final(SHA_CTX* ctx); + +// Convenience method. Returns digest address. +// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes. +const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest); + +#define SHA_DIGEST_SIZE 20 + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA1_H_ diff --git a/panda/docs/CANPacket_structure.png b/panda/docs/CANPacket_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd134316112bef9688d261cf33d3a78aa63be68 GIT binary patch literal 39535 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 Ny=8E-Ldz!X{{XZ5{Eh$s literal 0 HcmV?d00001 diff --git a/panda/docs/USB_packet_structure.png b/panda/docs/USB_packet_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..30e848c3f6a84494abb6f7a1f7169d06dc0aa6d8 GIT binary patch literal 21782 zcma&OWn5cN*Y910wpj7vE$;3FXp6gBae});(6$tp6qg{SSPLy$+=@$(;;uo8h9tOz zllFhz_kEr7oa=L*d9h=Y+1YDm)>`wO-%hlqx&q!4$|ra3+`&^)l-0g-2MdCEZt(~U zb6kHnNQ!xQ;PqNb_YtN9J+h9%yr%G$GxXMRv-S3~^t8ES=j!HS!{r6^w6SsZvUl@_ z-*1GAKoQ> z^-44A(Rj*o^Lvs+`uB?1IEm`NTR$*1qtLJT9zXu@(&B#ms8}HW{$Z;?pkH3*mG#z0 zZ5$sK;h&Po^47-a!-o%yjudk>b_xzDQGZH5UYd<%;9~02zjgjOBo?ZKsnP#iNwT1H zCi?dZ4<;;<|9<^n2gVF6h-~*m+3Jg>ZTEV+d=w9%N1L5O`}>Awn~nIXwJ|e3a$mkZ zSe-7K>fc_MURrLax1mkV@V@S9Twrgb{zdLFec-Q0%2;({dtZryrpZ>kG--GLM`qnx2|!u?o>-3K8cC~Jo56uYaN`i zK3djIah;DiTNzTrqN6gy`MyvW)kfHSPfCot;{v(ou5oy>x=rhs-oE3Ylco?PK znVap}S@Lf^Cq4+XiZtv-!wY#UqreQ>zPL4SNr`3&to}$YVkY(!u&az{733eH; zrI*N)fcsmx(3NtMgHF|V+i zD|g*!X2=&bFx-BTa^w8eU8}WG>)TG}+p{6eaw;Fzw{pLe^i0gVXX9=b!f4%u?aqA+sd7Vm!Pc~9 zg@Yz>5qag$1*Z?nku%xLg6xpwA8nYq!`LTn?6G^C-J8o%f9m&Vyd#|2-g2x_?Id<3 zaOvTYnS!@ZtELeTfTaOzdZAbbYR^zv=N5B84&v}fu+qS_+&xqM3c4YvnD$y`HDOJ~ zqJq&Db+TDg71-QYtw3dJ@j=m1w}1_A4+I}L5zdRwIisz8V#8Qe&my&hqZPVJsjgs* z{nI^UkFjZnO}gwKBM#V#XDUYcVGBBNSy`#44i7Zl znB0iE-6%IN01^~jsWAx#8cW$z2G!`a^v)|y%x-HXjw}tzXAao0*wv>}&(+5rRt=d5 zlIu@2!=7=^&3EqpzO^BwZf1_ z0WWFov#Jpfco%aL8D8 zsT=2N+}&@JleAUul(^n|c#UeLha1l}9TE=)c6(jT8iq{X1`xO7g%I*UlgXv7QokF| zpd5-e8Wy;QQn_$c&IWHJTOTrq*m3Px!(%}br2UF_wZmZ)#7TZq2A1*1tJ>=zTw zWBXI$Kdz^fXP8|q;JN0{wAb$8NZG&2o4U<;Z9aEQs0XdLxgLAnBI(OlrauCdjm-WK z=c?W~-!)4$Lxg&Zn#K3ctyVOseC_}|wmn@8@86~vSTXiZm?`8%HS!J5!H5k=-JX9XI-L;u z?9>sjX}INzxXhO!5_!n@Vup}yMjJD?HC`~#BWL9@)LK0$n~iNJ9Mt@? z;d9INZ>tvgGiD4-Z{ZGomIC!MQZS$;CqXF6@HGs-kY>~=%to)sO#c;i42?c0svjxM3Bks zlA&T9bFTp%I zn|3}~=b%1-lb=Wr*0lpYu$?4NSc`Jww%fVcMjZBXzNj!ai9W*ppODb~WgS!8~`5W5eA@Ua@ZF#Y=s%1ZfVOr|~ zD7zE_>Z%i&ZSu<}Yv?_i5dfminf_;hJHuhc%Q#^k3r~#n}i8ir;@Y>svEkBb?VR;{hYY3YWU33 zgEq@)e;G}lNDE?1kr)(&_CrnDutwDA#?B{XNZ?WlOdRO~z?U@I*QZRw96~nh9k(G` zTb=Au{=uB-j(C>#Vcw6Mt!G;w`8X<=60gu#YLh-`Qm6z@OjAMG&UN$E3OGgUg8I%D z7~J&ITmo7P8iTH{rVwyok|@yvD28JoXVaHVWni1pnZSGbKk z1hJ;fB=<|#S8?R4IRE48HA7A&w+#F$Z3=gOfxkMNk1FK!5_y_)H2!PO7clzey(UbQ z043$C;sm5SymG^x(YWnO!)vef9PnmVrR{WrrdORs;^DI2uEhK@%uRtyf(df{+7Y3! z)yp1Dw25>iU&W@~^2WO%$Z}?<$0E*@2Y*VkI#r0neMHo04Am=&@%3JW{b-g-bEc60 z9Uz&r#Y~5e32tn+1MWz_d)$)yna_RNpsNPM)92$4<)v-fSE@Oi38a?CayxAA8$Y4b zmYu7$nX5E%2Ugz~l_BsF9Cx=2xIeB6ZVfS0Nbsa_2Y)|YUiLSNINF;5UYyT7ZpKeb z7g_e;`h5WC82hE@jHXZ|-%QhJXav^3I`3ILUKGu&6LGUvX(ac5Vf+l_6gq0Ra{a1C zBi2d;W@eY{KEVyM*fiUZUC|FN7Amn*igkY!cGK(il%^bK_b259HmDMQ)1y1OoQ<8NUdqdA zGiVH=`02jcbU*CH`YtKAIig#lab{_zwtjBCZQ#4jQphlu8_t__O2wJXWW%|3fL`5* z^&xsg_do}X>j#xM+%q&TWS>iI3~lR0n;RU#Vp6}ERAuumwQt9wH!rLQtO7`5(FXT; z+<16r8pzIUtpBVSkdN5fn{YM_&N|dbW+G9CaY@G^^;80-Y6aOo^(#=d!P?ITul{rl z)8CHy8NW(BcO)2EbpXB*E$vQvD@st~0kh1`O&v(Sv7PX_dAdi=xhbL(BSJ~`Q(#Nj zu#jCZvD1uUHWc|_JNx5qF7Z3gdod(TWefLw-4;|Bt5{@&+h1P0sN;v{KF|Il&_zu> zA1XSJ=bRI{C4eXqDEuZ7W^~`2Y3h58kypa-n&nxx;Mu2z6)+|v;tICqZ=youp1tsk zPAZI67$I`G@(yQBp%r$$2`d=!w2nqDMY!F@yUZMHu)TQ-Z}7DMtsmG}j+n%@P3B69 zgrh)fi-d!Zu{RPf??=TwAAdVMK`fEzY}}IZaj);9`J`sZCD@MezH*7xTLaVLoLrd# zQCp9D8I?e438&{vq1YWD_LOA&qbIoZo8L=ZjO-_ipU~AA^-pfH9GMBF%Q%=>{)VDG zSTq@r3>Sh1-V}4wKqrfUbX`5)^R&u3uY!_|4x!Rg-(0LJQ}#FcLd!-lBc$`8`nUPeQ|L8p=k`{gYm;wz!34)dG67 zthz);3p;VT7HX3S6Okt}EED2c5l2-gR2_BI|6WF`VYxMuK%<+&e)V`6`VGr#qOS{M z4Pwa$-szQHaE!;R&hht%$qc5Iv&sp~H8fdLIPu7V)^?e3frSrOFjfoQn_FA?{B~bV}+b%s)gra*zb*Ob<@!wVQM$zIhiXhUxSDt#wblZ%?;Gj4LE zE5Dm#FQ}UGfWaqcRV`E3F2&b-QFRI{K|GrDIq^}k^I-gBU8pw)#%rWzP-J~rKm~D zQR^LV>Nm3$`PzpPyYXoEOvx$R;`Q2;Qe76wkSgRx*Q<|9D*y7*HPD}?P=$M57@AeQ zn2MEFX-e_!9Hp^@Up`cY+)8X$3vOTeadaDU|Lj1g5}%(gez*Pg6tpS$0z$@bbL#fN z&b2f0mgAWp!;V>89gPY??M&%M=v3Y^O)g7t_g(#XnhyE)#fDq2{nPmc*Ey&TcXCf9 z1>IOhHdU=a1PUJE5G1y9PtTaldO@W5++&M!aGFQ?-O#`h&T$`EV=}0q4L0{=XF#iI zG1wXI1vj$w^9c0`{+X#)XYu9~-qAfsH`W>;qux4`wJi|n83Ol|CpeFps$zwm6ym_{XB`KoE zEqH;};zFn^Mx-EM}Dw42^+)Hem>ewZRhKj7sC{~-LM zD(1R|uJ=l#aW6&$LEJ?M_1S1f`n>UGnK1mhB?C|7u=J*s_M| znHv;32?^QU5=bBVvtCgin;pW7y&Spv>O_6@=yvM3f6W#!7-aeSGj+iBSj8|-t}FBT z@DwvZ8MKYgu*Z-I+v-aufkRi?UJ+ATov$Z$wsReOXK4I5K1=MhEVLRl!|pnnfqUdX_$6NWe1b}V1#vHEz>47awNZ*p%lv`z z{05P`M!Xwvs*ej?0}q!QmU?Zw^;@ooe#_bFtb?n91DMnjp3}kBwrm%Zu`~9W93kbf z`ivUBPF~M5mg36hrH%ay;}p1_>;~els-mLeXQ>qVXvOhQ*UA@DHyZ{b?J8$hwm+9= zJ!wnCY8t)|>_@zyZA|dw`r|&l@5cF{=rBQABd7hhvVq(Oa?h2 zNYUHo5Au%HEKlYALvHX9!mxpPjwxp{pnoCz(EUCh^?*g6ryfcU!N!igrHa&?Q0<_o?pFqSQ2;Zr<5NXY zisF8XLNsGGk~6VEG6t|`mJ%7=t#5(2Ys=10= zf-oolwXKziBBke2cjyz!Ki#SrA?h$7EcR(OxcN15Ls=VPDSFoT;12@SzqR)FXYArp zAv8M#q^a4FFs-jA>3+MLko=8m`5ilGB6)lvndDXDmq}MqD)qj&TE&E0{`WIww7Cj<;@{&KrU;vm>xGo1u19T+ma()Kv6aKQ80B$Va z-sdU0iPE~2Y8mMP`QLZbrR%6XA=_Mb9{OmB=U}=)DSb?4U%qj&ccW z&uVZ&WWx-{e$I5X=R+rcZ-ob^kqvf<3T%Aqu<=WRFD7Uqd1s|p@wDd1gL;{r)6XL9 z;N71%LRgh2mogm6j01`(ZT;Dn7Yn3ck*Ay^WIO$z_<+x5w~P{0K@i6ZTr0co0})S8 zErr~Q@yvn9>WLpKtiACU*&jNoxlzrejK~dbS|*~g~BQvorUz?9>)5i3( zf(G%f3-g(3hI_kG-L2xjA+M5Wwg`U#2_YQZ1%o&YwCl@qw5hUD1lyKvaDfyTeNaUC z8}$-9*Ie2k_9LhP94&|0M{Q;kIea_;Qs0Q=lHPXj#C8hf7f$>R?LR$L4nePjg6`NW zlirrL9G$SI9qh`tz4mqAxAKN7?GreUWScW1%y z18l!w#}I1P*5}=H6wGNFfM35pI>Bh3Lf4WS$IqqwaTt_Q6e|PDKxN9h)^K*pY>n60 zN{wInuV(^2ZnOKWLb%9Xd;9(!A=mG(j=t+bxIq^8TOe8&Y(LAr%r_0=N8)ORGrn!j)Kx7m2}cne^u@yFEfjh4s+Xu|a0 z-?u0#^lp6LAffJg{-wB6nJ6Ei%!oE=#hO68*4=nJw8wp6X)}jh@;nuZOuk!_y$FiC;zGU|nt`=x-F=|P!~XLz-gx82fy zjjE%dYu-Q&;|H5i*_Ux{)42iyjezVA`=Ucr-J-(u3DW03Jfu(4e~s}v?+@FzrpXHscLjuwL!+cbX?sF zK=K;LO@O?2zf%A2t&}p!xYo0VxT&-%K&>*?m$hVt(J5G0S}s4A_*knR+qj{Yx%*S( zj1;}|j=!*i^0$UMau3pSTBn5I>m~$a9e|XL+*{ytT=WeA?&-d6U;#F~B`flGXkYV4 zUh^rT!>H8x7ut0Wwbm|J@u%dB1PW-iUnKW<=z;c_G;JCE0W`Yr%K14{p zbb$ocI@Zx^vltd>wal4%e#=t_$5x8B=EF!`oY}!ewsxdjSr!213tZ@5f*AeiJ90j& zCPDnL93@h%yW{sW>{E7TfAX*|P~JR`Xx`wJZw_02qA_(zm}(0+E$x15MYcTPV~IU5X3L%vyQ^!Kg!Ly>Y$Jm+qi~c;5uX7nxq{quv4dT- z6gliG1ZSL+{%m*Z7c>md)1fM~e%OI^)Qq^#<;*B&P<@Rqk~MzIZ|pld4ndIfJ}#;X z;h|^SkMhLY(^DY~ie|-6ZITr{B{|o(7?K1@aXzjb@b4MWF_yo3LrnH6qw47BM;|}a z-KvTI(gJXFCX`4#CF}4p&Etsf_(Lu2;>qaVrIu>`l6+4Csq@79S@Oh%(x#(oQ=)B! z7K92;bcTq3eDvEgwGaom9&+b#gF)eN*JcTl0VX(9KwGU6*G+wo@RQ3^-0CZ2npw5fKdGIsnO!i@hK!uZ3%m4NF<_;I-*v)4N7aI7^u zZciXcQKBa+byJKZrBUa0P5fnd2-%QyLf}sIi15sF?#?6cup@zz z2Kzhz5XA4&nc@yN@zR{K&bGb#p+~GUN%C_rDX3=z?UsGvTIVrVA>y=GVvxb8ZseN! zaCI`Vz8tZ_p>=kK+xi>u(bM!()tr|bK4ySe?VGM!EHFj#-d!;Ok? zrtn{0m)PTl5{Fre-9R#Q6Q~86WhiiJJB%DAI};Pk@zYk+^O|0LoRCYlB5!-c zC`NKIf3UDJ(TiP0A%Us`esCXHzcyQc>KmK1Vs{ zc3ooWG_Mz$+^HuG3%ze{b@oZy9zp5>`Y-24Xpm(6H&O_%UIJVCzyWAy44r zY5S2!Zre`>Z2yPn>w$X?5b0XT_9hDB1pPK2wuD^d6nw=&Y5C+eFCUMljew7RXUPxM zMn2Ce`Q+UsaJeuei^91i@ZA?PlUG@*h}%%+zTpe~My$!|luTOxLTIKb=c1&8N|-ZX zB_e%;tn;-X>_M}(FLcuLeg;mN;1_|y^LegTB&Ejfm{HFR@~3`jyl|NEgI%y=Qg=hpw>d8z(LC}In*40{mxo_M#}fXF?X3bDqvuc} zglWxphX=9bjS_x#*84jko51URt)X6XDfG3MvK}6Pt+RQ>Pe`65_}4lY{{&@z$K^wX z^N=(yiP2CFQOc2T3&XjOBram?I$)F_CcGhOlHi>FqgJQ$Pf!5aEe+~@8|z{X3dwO-ROGhv%(#G)$dbF# zdB}|#(XW1BaBobmDT%dHq{e-q${yjdyPfI`J}Pwo+Noum^ya~os|wdGij!t0R?AC9 z$-JFZ&uXy?E*~Ts)j~?g#-aJ$o4<2f)wcJp!jH2EIIY5VLU1Okge>qW`6d#x7(mI8 z@1C58A1`gyY!EZOOp*G0gpufsPvVJr8wO!bou;6+1eY1&{q;V4-pa3#w%k91; znWYC;dhZ8ROXkHmZ=%L4HSla(0C^!k3ZBJ*V#-(xjYl)wZC6%edoD$+sO2SXY6&?B zk;3KAiE)lc6n5fiMY8jXuR~0F>SMe5&(s;RY;-D^qr0{s(XfKuu|sV?N^-o_%N3l9 z>((BVkzd&(@$U=z6b4IwI6NJiBqYuRjw+3&jlyVx-Oe9g0%I2kTC`RW&f&)!UykXy zuFfp1Ho?EmT3?hbI}M{UCB@qP#0s#3dK`_3*-uf=UDefHvxe7c$UW{Qd^9x?R6loN zhPC1S?CgYPvI~}dR4O<^{%Q;g`fxi!F3DWE(k$wRbvwN85!&Y;7JlY6Wp5%Jwk~6m z1?{8!e=@KedC3W(^{(=j%EROkUA9}11FCce1$Jx}KQ7?->_cib(@a9Qv^CeF=!PeM zgYac=CX(HQhMSIOux5j84O#UCu%B3!V4htl@n$JjY_$8$lK-uRjqOlSgdThtINhW< zlKB1`lgiQbTLXuWS0ki98R|##J$vLnYqI)Wl_ zh32O=bJFG%iU{AJMGxK*8jA$K%*{X$Hf5CiEb!Uxk!stPtKZs8*eCa_Ur)n--y#&Z zuB#yo+YAW&p;u#$T)*eeT+D{N>x-q{n+I-eU36TzcV9MzyVYD|U!zk+XHe<4hG|9T zQD8-97e*7rQ#SnP9AJg$AN%7lK`C-wL z>ea^mh!MlCL!d$B)%K?i=)uLO6{-{!sv@GTdkZB&y>H#Ns%@`A)Ja(OwjQr5;O4GTh~)13g)Ejz#OfQ4@%FCjgwt!S0P$W$KX63nI_#EUjRw7l5M zI!T*keXMK}11?kxQ&WnxIv6PIC@$0h>5dlzcE_~MPp!3bQyWNllpt7;T)tZ~Zf4mZ!(^7t*P znIAA+C%4Gl#xW@99u|5g|~{_Mp4TX|gkFSrDHFDAxUBnHYI zR#`CGj%pf2hl6PPubj=^Xh@SnQ6PJ%%?Be2rl$X(J4VGYHBaN&cH?K0m&D38doPQB z-X^xszHEw{g+}-HnF62tBqEQ@DTS9r4}t% z`Zf2q5z)dhADnv|xV$ro_lZe3qRae@WwxG_0;y&(buk;l67h@g{$q8NRG6WJD7uo^T`YHk!_#?LZ;!QUtNBBTnG98o$f%tZw=9A|#%I|J#f2u_my{ z<*=`}huqw(qhy$iNMsz4pWo6s7qI3brY!7ex@nOcYy!?vRj0!#vAdDid8!V%`j2&p zkgO<{JDl#cm*LBK=z&cIoqy~L)jr&)#w8cI8P1LL*Gr6ce|mM;X(ZxJCY?JhyKMwN zGMafZ>86{=ZGN@^(f@kk*U8xnpSt&t<=0DTZP4Dyfn<)8$FS9NIK}0{Nu)vLUzkU9 zOoA^=q3&kVAyKq?9f|ih3&|XAbXjMH z!)XFq+$fLSwS`@mfscXw?eelbEZ##e-|MuhRhYXLvu-^RFn3Yu{aiEqBUBB|28Rcm0Ran{Diy+-fp&kAN%Zn4LtJcb3nwzZSBLB2g z>nFco=Dw#Js~eR^w@??EZ4v40Eweh^^wQaz9fevCipKO9ya<|lfGlxT`$}+1c=u@= zxJYGmP5&B{wc&N^MyJm|_=EM8@U~?dH@;k~bX3C~ueb{Hqwd@S32B$BTy;H9!j#VY zqZ_X_0`eJFTw4{7aR@8^lNeHDWgXDpuRP$cVw%oY0yOLW{Xh7o;;95S%XIu|JY{%) zHZui=IUbF^`H(IV+|@h5hg}NO%3H`g!EMKP>Ozg4Ck}PR>So4{9ZisaDCHwcG@fKG zRZCHLc`-qAG2!AHe?!M%)02=HSD?uXiW~G(O(U!6++bqb(zZKpQ6+NVK6hcRb(xW+`uJybI z1iaOw%-Z$#gO!PTtQ&XJ3_RbDmoWdFNq8sBh5P4{duP{OX%FE^XFo;7b={!Hzl?LftW7SS>N<*kJQ>&< z3q@+{U-Ra?JJG2`#BS*Z1Mk}5T0mvPg!K@u4RO&mVEJ~!CLm9Zt!r0i?F%jrW^j>I zgcnDXc9pVSaUR>NdN*7fA;J&pfPlxeeypXh&kEDz?xjfzYO5S)v`Cw`W)6EK`R?EfUB_Z)(jM zzyA97j?206Fj!6W%(a?lkw*BH@~dV{#gPZQ6+Yq7M{K`FSaN1JMBm4Ji;5)!IpFv@ ziQHymC~fj_L#B30zuYUZK*}D1*nH$`=f)OW0Sgtv!e|u`nudG3hncy-uFiRN)bJlS z;Y`7&)wYmi$IV5>RHh8)yN7SFUx^7MvOw^%OEQ1iJ(kc;u^!6SpFD0h3ivFcU<@dTte3SmkXq}6aC5a91W-a}V5u>4bhyEKg9OZy} zeyZFTk#2#ZUFK}N{GhQJLHw3E?$q*^TBBhbASclqyOy4HVTY=9Q@ZD#94jHUNwF03 z{K13REg96GaBVoYx{RuMLqGD5foq}|@f?Y#n3d_Z(cUs~*KCwPLd~)laDxT4be=X~ zCscCh=-WbYh%zCF+8aOL>k*-nXl=fu3=pM@N0WgHt=Y z*%EXDZ$3|l@^FYKd~8vDF~|(w2r%p(R$z2sCwtz$U^87I^~}Wq85Jo8O&&PT>JfxCd@G0>*iAB3&N)xXKx+?H~lC;gkg&Byn>SZf5SaTkKyAU*4ymhn@njw$oc zpKkc6LYGx%gQxql7rT3(Vh<=AU zKhLe%Mtchn$Y$cLEA32k^*h}RU?gU4-aiQ; znS57tfWg{At<%|N%{~Of2XW_fyP>y!aWl>i0v|{4%Bcx9J27_pH}zeefyjIQLcK_C z`mrx+BudnSdF!KGUSd**C!7i9TfYM5DXf+$UdoyzW8Azfk-E#5?qhGR0>5OGD}@qC zRU0&eYSeh~jOZ!BP<3VHQQ1@+BB#3da&u1m;X1{^W2(A9-AS^Hh&zzAenhOO61k*i z`F|4~Nj;fldhU_xB{AclJ1vMvJZRIHlo$bTo5#a>9#ohBM9g zXvpMN7XPz&>n!TFxjy~zm(lMoK(>J$_ijX16rgsJIfjvaQvEk+2Q6-WJ& z%G(O!&1*l`1Wdy1%Xq|JkGcbhvf#AhWPyqFF*2cq~2afKPqkd?j}fh5m7U_cln{AY9Peb2iMT2U5A!??g(S=5vpkAqt@%oW(TEv}Sdt{#|FIGNrpDO-0|O0~ zft}WrT+n17#{{%N^75`QVIz-tlI}2${gI91Da|muca^mqq-=|VZ z);afwxAN~(naN3?EHt)-2tX4QZlc9z+cyY&#gep|v`?whN9S6UQU@c%LUV~NwN?VM z@(gGhl0GyejQ!Rd8i}DI`z?Qh?9(X`%S@F+x9{*PfQ$v~Mh2Cn9Rqlryen`01f*$=`y@k{QHQU!C zt7wb?xlFZUY^yzdy7;HfVyYL)AQ?5k-->$^MytDgR{X4q_Pzz+OcjPkdS~ZK#^+Pf zAGW^3Y;ga#`MPw3Wa|F7xbyZK%X?u#erp&uMV>%SPzF>FQ;e*$S3M*LY7?95+JW) z16KHUN(35;+Ri>|c30GV$*Pd>!J=!(9GYXBZ+vkw9?4(YVdqWNl9ibpYV{KJu0@h3 zE+~g;jB{b)M2FDBM>r3cnno+VPR6!sJza>Cb|Ks#5Zyn8i?_4L`iyEdtrf7zZ$zDg zAiS~tZiA}e$VBuwwfqVo$B3i4B|XWdM@+iv!qU-B#oGEsYGD@KTsi&OI&=1m$P0<- z=6JJ2=N~s4Tu3wX?uCjU+ht;9hF4GmRdgX=?Ui$P*?!=0IV{qr{ltSuej%agNJ>2V zw4x0<3sN!S_aa$xtc4l0FN)YE{|>NG;?K^`7|#ZbZWSGT^BN0jBH!|P4zB?@NZ#mj zj&A2)q!2g36#!qF}y2KdtMZEye312=4o|1N?FsA7;HHpmPz5N1^3` zM+$*I2fU()i08yJ&!6f<1W9}F!^?*-SO}F=rpQ%%yEoFwJ0guw_EruQYL-JiA_O;BQCP4@+)rsM!@fVuIB#rmVL>IM6jR^m!vOF5 z_1f6zhG^AIubk1QH)LK{LffT4KIjUYe~rx1XJ?4*2IW#-J5rN$f~CddS};kt$VZb zsCerl;k6HeyQGO^6C+LUPWPJos-RYAJ=ReCc7fVP3^FJPo{XH-fqW6xB8o?r%%Vz~ ztAaccBX*-TEn}1kj2i_}{RIVHk8))CiRC~u48_8_F7~+7h}dLrjIt$hx}K?*70}(2 zF%eghndl42s{B3c@8}P>rl+qvDKbQKoQF0>ZdTX@4069C1-bv59LO}73|x@_Pi##floqBK$<+Sg3JX({p*FV)a6?c~^W4nrOM)~{w${%|E#9|gjxHfqCYs|#d<%PzRMC6t|Mjh(<*isc!kg|4dmg#+=aua z4PnJBq96ZP`jU10SNzgY?Ry9B4QFQ|k80T33f~VsvSDh%#FDgmSS}OEUHf?i zew?u9^#I=vJr${`I;72WxOUU&qL{uFjCJ4*C~OrMZ47zXx_erz*<1JEs-8vT9Y%h! zZE$K5U?_6VZ!%k#_D7q+se)k=^9Yk%DZ$WfySm)q1`)zo(K-l^`GNdaiVMi$Tq&Et z2mbUwNs(UcUw2wNf{YFIO$bEpEpP=YB0Q3Ox+FB2+yb-%0+Z-j$y4X??7DU$J-V|$ z1Zh!@Hb_PKNfw)f9`$rvaDV%(qfX#_c(GG+V;S_~P9pWK&4UU99z}O{9soe+>A9-X z?pao6LM#D~)W{9ryF;It50M4R5Dd2D(Xwv={wUeg8y znJkFpwZ%g}GW^2kfu|HgO{42%^odK)qh2FCLP_YjW%$ltl7yWVKWo9>)FRL?MScm? zrX|(bEm+?f*e$S9-k0KF-ZVnwpEpX2_o?uc6cEVr1-L1_kU#KfThGZ6mYF}Y;@mz< z2!l`nj(PZPRl3k6k>!T|dr+aLI%Tyzu=^T@wH?N+! zd+u}!#Ysw@Tz{&vB`>>jCO5lh2#-6w(RMrmwN%d!4ip= z5FY;9K7;=4i%)r*;U1<8Zs*j0t$HLA1s328ZKki44^L_~BF8!z zl0DCD#J$>eDLrT#ycCigID3omKwQgZpPC3#&4wfS2x5%MVx*7GXu=wQ?k>|%+-Pjb z#DkM##gRcbh;092;fk|8*<4-AeRIjgwJ>ANwR)Xz#%SS2I$kO6@3(IUr?9Fed_eQ3 z=Sf66xC+z3qeoga{+2MMkc-XWCz3~qrp{#t_;~Sz$Tf`SqbS0|o_`oxZ@(;kaXHZ1rnzGpmx z1w`tgNl8|4490UHmUf6I6?#(6gZ2OLcNK(}w0A=K2UdAvb-n-sB^G&DIVAAuCgkDL zMGK6CcyD<1b$O}krFjH48eQRLHgpBk_;_)WJ9H(;!ghyMPOQD7jqkCn1u&8?v}b^etI_{6~$R2ZQAR)Cnoh~5p#>|(xL&3KSgmGa#>)&>ky2G zWe`xA_XBuF;2i|S3CU@${I;l0+0wH9?$ocueHt&P2W%%WX450#&EsA(WpDVtbD3`3 z8bd$pN7T|62`XbYZWo&2zgc%rz_7o6qVQCeCGq7GR>`-=W+ZGe#|h8n9PM8fvIelN z7mfG4b^{o(q*kLUE%tGubaCN{>#N*;c(r*O!9|Q&Fd-DX8K6mS#i}(h>I=yn#(P+ z8F1}$yDaGby(^YZ-DdM>dgx2J6+AVFq$zi!tGhVmLl6n}fJOeE5|)~CjP)30yp%mH1>KXIWB9KL}KtuxLx#<`nK!r zi^xGM|zLBiT zl$2y9V)W3vBIPRBkG{q7A$6*t>(bE614fznJ^|GPP&(d3DXapyiy*e?sR}&H6PKyyki$F@V%bIu-ERaMW(*v^b63{FVXMo}?3wY>cJf zS^L24mmX zY_F^{_H0qfnuP3wv3=^F@B99_e>~59o^$T`-DkP?o^yc?&R?38VxzQ6zxA0~bc8=T z|IO~CQe}sOeB6>W*C1BoWpc966~n!IATbeP5oEm8U%YhbHZGXoc4Z*-2cVe}_eiFJ zIVpF{wK?An7t zn?-Pxp=kQqtA3SC4SVm>SohOC zjTK1Uz^n0g@Q~`a^C}(bt!RxU7`&X)e1Apb7`m39xlS+qP(~C-+`S=^ga0|GbA5w# z?Y!WLAA(h+>*b17J*lnEfT3yI%4qDtr8x_6;cuDEMymAY+l==YvAm80`;1H0*8_?o zc`_zBy9aeI51U=r^7;0T@+xB?$56q;!E5B4K06KL_N=rN7Kas~_Vbc|;*(xme?f17 z)hnibRy0xSsyOO_V9&yYkJrMFi2TciTgkc7#tGr`DBL>z`OVhO_>x_cx4Fgc!9};W z-taI;)h^%DbWv(4lRv>>Ynj}D>o+sojVxv@O13qkB+Lw@`vfo@M$7@BDS9SB*H znfGax^A0~i4T6adZ7-O)rqLLh`ucU%$5AzIT-c`%qbSJQ5|vnG!zH6YATCXp99lt6 zuY-(N*!JKKpjGm|^-ZD8r20{a~(aF)L^)sahJjd|B`wpEwi0!6CA0M5JY3 z^g#%l9f$)L^W-Szd6}Ykj+LZ-qD+iW3f)iN^ntf;_mjo9HzK#Plw)S8M{Cm+Mw<*=l{if+$0oEf2h}C-jIqdAdWx5ixJ(->N*v_<1Ct zM(5U6j$a1v&uru0u7c|-D1Q1UuNH_;a2%GKtz=!VIM%C%Ae5B82V*YwR`L#_R+A>d z(!-leXpTYT!80A`$ygWa+e} zKkhy`+VizOh2X8Z%H(eT8-7?JZmt-{VW;|4=3_eG51C{J^ zN$%BxPSM-@)Hk-C|0F{HNi#K(|JQ%@F|Fh7+*prs@_f>D;FYrd2^#6v46mj zS(1+OZLtTE1xx;UaGm@3c+3>5tK(?joBPZU@BUi+Y#*k>R;usJVzUvJJ2smYzPdoy zwHlJ%vIulHN+?t)O1|FoDoJn zKg9}Zd-uSr2_%luv~dcqUTvq1s@_hYxbf|oyz2<#=u$cx{TE}s*`H)&FkembIFo95 zpxs>p)S(1S&-R7(T`G$G^lg2#`@X*xFoQ#4j;-o)Op&k*AhNhGO`Jc^d&r4i#;D$W zEl*E4X`;;jp$U%|7M0-IADqa~KkO_d)o|-6zr~qjF?~AG6+dd=FG9viE zmIj-WsLFoVzq<8iX|i>2gV3f5~gx@KlKcba;zsEDkS5BWs#?>P)cysAqW_^|0*IOOhv8a`3-ri zzqg}^yoGx?PSR3Ft7TxnADuMP~48elB#$k6?I!9rY(!o-CS+VD4R zFZ1yuH1IZfQTfC2d%D{tE6C5%z^`I|E$8Ir;Rpo%M|t!Nk*hljCL=rJy}Bu!1m7Qh zQq56!oZHMor$CrW-KA|>kZv&^YicCDrb#RXdbWOL4*k^nav&Tgzg8=OIzG2C0?=zp zPgV!gE_j^Ub`%8PYk5v%xxa;=BflzC}iOVR=?~MC~z^X(8A@ zv2=-yU0emLi*E4`a+6W?BE;G*0-vH(SJ!o?0~s@+a);-dB>|s~hT;%WTI(#q4-m^9fy4QipOtiNt$tP8YNn@25lJ-w7ul;M@!DT|Q$ z0fxl>`_JpuM{!1|np zHQYaMVjgTG5>*wg?ubo)H);vCPawV+pR@>pnykks*>Q{ycJ=WjN@{;<*b%|`n-n{#K0&h;$Oy~H5iR-DRFxc{^R6`12Cl1RpU%1u7$ zf9yxc&E<)XkKOdaq47SGMy5qq%=eeQjNeIW6_t)j_#7$^gMkaQ86J;r{&`g1c}0M! z>GN{A=})?uGZhM}jo4o7!b=6JWSDfrY*`Z z5d1@6R}Ad?oYW9qkigJaKJa;v?%9&LS#ly>Xr>{GWUrTM!@ zT{`8Vjt`nvH%rfTbE>v3#BACTI!53 z-d>m=1X8^~pXtBNMB~j;u!pCFdv!f%d}eienNwX?x6C#oQ}D5rt#W15n7H?GZ;baV z0jay*V+}&hjZtuDT(G&$mTi#;njxNbD(!Nz#xjxlW(dAN9!hZo=~%ow}r-SCS5F@aCn&9F#avU`8+@_bYpMl%@n*Y$wMwbh&Q}6u=HeKe)#%amYI_y zs|ix;?3zLJC)46_v*ui+2})t|!YC^-oZ=v_5XzRK;H%y}{Y_`^+7;d*B6FKV%oi}Q zx69ZVH-m_25dN@b__X@3@pbKfYVb#|G|H{>?eoYN1%+DrTYs+KZbccQM5_{!1Bs6SzX2OZR>Ye2sXOzMF3_gQ8!Y?)Dsz{z*J9r6* zV1d>eT@)^t9nme$ng1wR?Gh9o9|E|tK%gw5Gq3DUa^=g%9B7S=4$W8>2;RT-d(|Ig z9fnIIk--+}0;1~w3%{VsH7W0m1J3x)f z?|4hMHjX>>ja-{NB6l{Gotto)8X-Bqh~Ddk&sFqyq=on|l?a{ilcTv}Lp#NRK|~ES z5(TE!6=%JRl8?FhjdWUTeN^RR`AdiOT|wB@Yj*Fu^^qnBrE+%oaz~$)$2ce@7^IW(IJX0X2;k{XPE6Xd_>Ma=zc;YCinkvf zRf6kC+uHCc&Lffjp6WcruJhPR*d2yGxfj{j^JWiD9HC#T6MGg@eyD{Md~O7?kZQ8C zUr`7I6NU6`u~aR;%GD}lF~K*^6fL|Macju`Xa$ow^unoxD(wy$xyR=~C_d8cTPZUD=&_hk zr{sNC%Yg42=g+1crS*Yi?Fq^)aW2xyhm?9ZOp1D?TOZbVpGA6F%Ws5u20LWp-cjB% zs}1L#IrNtYp=;wl9pKC1Q_{m;X60+|8Bxd9v+c*=e6TxXEEZJ^O;`ynMbqcoxtBoTslKf` z89TYaKWMF|v`cx(u;~NT*7SaMwlM%E#2yz3{rIl)F*?GEIZ>m( zB^Sp3!D3}U0)&KwM87xV&Lk2p+`5bgl!MYUJq$t2;Wx8ESf|c9l|DVPgDRwROf6RV6(9U*#ak_T* iHuL}bJ5x07hA}G;!Z-ke3}+vk(&@pBv`aOgMEnOon~Y@u literal 0 HcmV?d00001 diff --git a/panda/drivers/linux/.gitignore b/panda/drivers/linux/.gitignore new file mode 100644 index 0000000..93a6cde --- /dev/null +++ b/panda/drivers/linux/.gitignore @@ -0,0 +1,6 @@ +.*.cmd +*.ko +.tmp_versions +Module.symvers +modules.order +*.mod.c diff --git a/panda/drivers/linux/Makefile b/panda/drivers/linux/Makefile new file mode 100644 index 0000000..5ccc4bd --- /dev/null +++ b/panda/drivers/linux/Makefile @@ -0,0 +1,24 @@ +VERSION=0.0.1 +obj-m+=panda.o + +all: build install + +build: + sudo dkms build panda/$(VERSION) + +install: + sudo dkms install panda/$(VERSION) + +remove: + sudo dkms remove panda/$(VERSION) --all + +uninstall: + sudo dkms uninstall panda/$(VERSION) + +clean: remove + +link: + sudo dkms add `pwd` + +unload: + sudo rmmod panda diff --git a/panda/drivers/linux/README.md b/panda/drivers/linux/README.md new file mode 100644 index 0000000..2280ce8 --- /dev/null +++ b/panda/drivers/linux/README.md @@ -0,0 +1,28 @@ +# Linux driver +Installs the panda linux kernel driver using DKMS. + +This will allow the panda to work with tools such as `can-utils` + +## Prerequisites + - `apt-get install dkms gcc linux-headers-$(uname -r) make sudo` + +## Installation + - `make all` + - `make link` (optional, setup to build/install when kernel is updated) + +## Uninstall + - `make clean` + +## Usage + +You will need to bring it up using `sudo ifconfig can0 up` or +`sudo ip link set dev can0 up`, depending on your platform. + +Note that you may have to setup udev rules for Linux +``` bash +sudo tee /etc/udev/rules.d/11-panda.rules < +#include +#include +#include // Macros used to mark up functions e.g., __init __exit +#include // Contains types, macros, functions for the kernel +#include // Core header for loading LKMs into the kernel +#include +#include +#include + +/* vendor and product id */ +#define PANDA_MODULE_NAME "panda" +#define PANDA_VENDOR_ID 0XBBAA +#define PANDA_PRODUCT_ID 0XDDCC + +#define PANDA_MAX_TX_URBS 20 +#define PANDA_CTX_FREE PANDA_MAX_TX_URBS + +#define PANDA_USB_RX_BUFF_SIZE 0x40 +#define PANDA_USB_TX_BUFF_SIZE (sizeof(struct panda_usb_can_msg)) + +#define PANDA_NUM_CAN_INTERFACES 3 + +#define PANDA_CAN_TRANSMIT 1 +#define PANDA_CAN_EXTENDED 4 + +#define PANDA_BITRATE 500000 + +#define PANDA_DLC_MASK 0x0F + +#define SAFETY_ALLOUTPUT 17 +#define SAFETY_SILENT 0 + +struct panda_usb_ctx { + struct panda_inf_priv *priv; + u32 ndx; + u8 dlc; +}; + +struct panda_dev_priv; + +struct panda_inf_priv { + struct can_priv can; + struct panda_usb_ctx tx_context[PANDA_MAX_TX_URBS]; + struct net_device *netdev; + struct usb_anchor tx_submitted; + atomic_t free_ctx_cnt; + u8 interface_num; + u8 mcu_can_ifnum; + struct panda_dev_priv *priv_dev; +}; + +struct panda_dev_priv { + struct usb_device *udev; + struct device *dev; + struct usb_anchor rx_submitted; + struct panda_inf_priv *interfaces[PANDA_NUM_CAN_INTERFACES]; +}; + +struct __packed panda_usb_can_msg { + u32 rir; + u32 bus_dat_len; + u8 data[8]; +}; + +static const struct usb_device_id panda_usb_table[] = { + { USB_DEVICE(PANDA_VENDOR_ID, PANDA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, panda_usb_table); + + +// panda: CAN1 = 0 CAN2 = 1 CAN3 = 2 +const int can_numbering[] = {0,1,2}; + +struct panda_inf_priv * +panda_get_inf_from_bus_id(struct panda_dev_priv *priv_dev, int bus_id) { + int inf_num; + for(inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + if(can_numbering[inf_num] == bus_id) + return priv_dev->interfaces[inf_num]; + return NULL; +} + +// CTX handling shamlessly ripped from mcba_usb.c linux driver +static inline void panda_init_ctx(struct panda_inf_priv *priv) +{ + int i = 0; + + for (i = 0; i < PANDA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = PANDA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct panda_usb_ctx *panda_usb_get_free_ctx(struct panda_inf_priv *priv, struct can_frame *cf) +{ + int i = 0; + struct panda_usb_ctx *ctx = NULL; + + for (i = 0; i < PANDA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == PANDA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + ctx->dlc = cf->can_dlc; + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + //printk("CTX num %d\n", atomic_read(&priv->free_ctx_cnt)); + if (!atomic_read(&priv->free_ctx_cnt)) { + /* That was the last free ctx. Slow down tx path */ + printk("SENDING TOO FAST\n"); + netif_stop_queue(priv->netdev); + } + + return ctx; +} + +/* panda_usb_free_ctx and panda_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void panda_usb_free_ctx(struct panda_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = PANDA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + +static void panda_urb_unlink(struct panda_inf_priv *priv) +{ + usb_kill_anchored_urbs(&priv->priv_dev->rx_submitted); + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +static int panda_set_output_enable(struct panda_inf_priv* priv, bool enable) { + return usb_control_msg(priv->priv_dev->udev, + usb_sndctrlpipe(priv->priv_dev->udev, 0), + 0xDC, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + enable ? SAFETY_ALLOUTPUT : SAFETY_SILENT, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void panda_usb_write_bulk_callback(struct urb *urb) +{ + struct panda_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); + + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_get_echo_skb(netdev, ctx->ndx, NULL); + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + panda_usb_free_ctx(ctx); +} + +static netdev_tx_t panda_usb_xmit(struct panda_inf_priv *priv, struct panda_usb_can_msg *usb_msg, struct panda_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->priv_dev->udev, PANDA_USB_TX_BUFF_SIZE, GFP_ATOMIC, &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, PANDA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->priv_dev->udev, + usb_sndbulkpipe(priv->priv_dev->udev, 3), buf, + PANDA_USB_TX_BUFF_SIZE, panda_usb_write_bulk_callback, + ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free it entirely. */ + usb_free_urb(urb); + + return 0; + + failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->priv_dev->udev, PANDA_USB_TX_BUFF_SIZE, buf, urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + + nomembuf: + usb_free_urb(urb); + + return err; +} + +static void panda_usb_process_can_rx(struct panda_dev_priv *priv_dev, struct panda_usb_can_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + int bus_num; + struct panda_inf_priv *priv_inf; + struct net_device_stats *stats; + + bus_num = (msg->bus_dat_len >> 4) & 0xf; + priv_inf = panda_get_inf_from_bus_id(priv_dev, bus_num); + if (!priv_inf) { + printk("Got something on an unused interface %d\n", bus_num); + return; + } + //printk("Recv bus %d\n", bus_num); + + stats = &priv_inf->netdev->stats; + //u16 sid; + + if (!netif_device_present(priv_inf->netdev)) + return; + + skb = alloc_can_skb(priv_inf->netdev, &cf); + if (!skb) + return; + + if (msg->rir & PANDA_CAN_EXTENDED) { + cf->can_id = (msg->rir >> 3) | CAN_EFF_FLAG; + } else { + cf->can_id = (msg->rir >> 21); + } + + // TODO: Handle Remote Frames + //if (msg->dlc & MCBA_DLC_RTR_MASK) + // cf->can_id |= CAN_RTR_FLAG; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0) + cf->can_dlc = get_can_dlc(msg->bus_dat_len & PANDA_DLC_MASK); +#else + cf->can_dlc = can_cc_dlc2len(msg->bus_dat_len & PANDA_DLC_MASK); +#endif + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + netif_rx(skb); +} + +static void panda_usb_read_bulk_callback(struct urb *urb) +{ + struct panda_dev_priv *priv_dev = urb->context; + int retval; + int pos = 0; + int inf_num; + + switch (urb->status) { + case 0: /* success */ + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + dev_info(priv_dev->dev, "Rx URB aborted (%d)\n", urb->status); + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct panda_usb_can_msg *msg; + + if (pos + sizeof(struct panda_usb_can_msg) > urb->actual_length) { + dev_err(priv_dev->dev, "format error\n"); + break; + } + + msg = (struct panda_usb_can_msg *)(urb->transfer_buffer + pos); + + panda_usb_process_can_rx(priv_dev, msg); + + pos += sizeof(struct panda_usb_can_msg); + } + + resubmit_urb: + usb_fill_bulk_urb(urb, priv_dev->udev, + usb_rcvbulkpipe(priv_dev->udev, 1), + urb->transfer_buffer, PANDA_USB_RX_BUFF_SIZE, + panda_usb_read_bulk_callback, priv_dev); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) { + for (inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + if (priv_dev->interfaces[inf_num]) + netif_device_detach(priv_dev->interfaces[inf_num]->netdev); + } else if (retval) { + dev_err(priv_dev->dev, "failed resubmitting read bulk urb: %d\n", retval); + } +} + + +static int panda_usb_start(struct panda_dev_priv *priv_dev) +{ + int err; + struct urb *urb = NULL; + u8 *buf; + int inf_num; + + for (inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) + panda_init_ctx(priv_dev->interfaces[inf_num]); + + err = usb_set_interface(priv_dev->udev, 0, 0); + if (err) { + dev_err(priv_dev->dev, "Can not set alternate setting to 0, error: %i", err); + return err; + } + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + return -ENOMEM; + } + + buf = usb_alloc_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + dev_err(priv_dev->dev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + usb_fill_bulk_urb(urb, priv_dev->udev, + usb_rcvbulkpipe(priv_dev->udev, 1), + buf, PANDA_USB_RX_BUFF_SIZE, + panda_usb_read_bulk_callback, priv_dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &priv_dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv_dev->udev, PANDA_USB_RX_BUFF_SIZE, buf, urb->transfer_dma); + usb_free_urb(urb); + dev_err(priv_dev->dev, "Failed in start, while submitting urb.\n"); + return err; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + + + return 0; +} + +/* Open USB device */ +static int panda_usb_open(struct net_device *netdev) +{ + struct panda_inf_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + //priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + netif_start_queue(netdev); + + return 0; +} + +/* Close USB device */ +static int panda_usb_close(struct net_device *netdev) +{ + struct panda_inf_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + panda_urb_unlink(priv); + + close_candev(netdev); + + return 0; +} + +static netdev_tx_t panda_usb_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct panda_inf_priv *priv_inf = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct panda_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv_inf->netdev->stats; + int err; + struct panda_usb_can_msg usb_msg = {}; + int bus = priv_inf->mcu_can_ifnum; + + if (can_dropped_invalid_skb(netdev, skb)) { + printk("Invalid CAN packet"); + return NETDEV_TX_OK; + } + + ctx = panda_usb_get_free_ctx(priv_inf, cf); + + //Warning: cargo cult. Can't tell what this is for, but it is + //everywhere and encouraged in the documentation. + can_put_echo_skb(skb, priv_inf->netdev, ctx->ndx, NULL); + + if (cf->can_id & CAN_EFF_FLAG) { + usb_msg.rir = cpu_to_le32(((cf->can_id & 0x1FFFFFFF) << 3) | PANDA_CAN_TRANSMIT | PANDA_CAN_EXTENDED); + } else { + usb_msg.rir = cpu_to_le32(((cf->can_id & 0x7FF) << 21) | PANDA_CAN_TRANSMIT); + } + usb_msg.bus_dat_len = cpu_to_le32((cf->can_dlc & 0x0F) | (bus << 4)); + + memcpy(usb_msg.data, cf->data, cf->can_dlc); + + //TODO Handle Remote Frames + //if (cf->can_id & CAN_RTR_FLAG) + // usb_msg.dlc |= PANDA_DLC_RTR_MASK; + + // printk("Received data from socket. bus: %x; canid: %x; len: %d\n", priv_inf->mcu_can_ifnum, cf->can_id, cf->can_dlc); + + err = panda_usb_xmit(priv_inf, &usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + + xmit_failed: + can_free_echo_skb(priv_inf->netdev, ctx->ndx, NULL); + panda_usb_free_ctx(ctx); + dev_kfree_skb_any(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +static const struct net_device_ops panda_netdev_ops = { + .ndo_open = panda_usb_open, + .ndo_stop = panda_usb_close, + .ndo_start_xmit = panda_usb_start_xmit, +}; + +static int panda_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct net_device *netdev; + struct panda_inf_priv *priv_inf; + int err = -ENOMEM; + int inf_num; + struct panda_dev_priv *priv_dev; + struct usb_device *usbdev = interface_to_usbdev(intf); + + priv_dev = kzalloc(sizeof(struct panda_dev_priv), GFP_KERNEL); + if (!priv_dev) { + dev_err(&intf->dev, "Couldn't alloc priv_dev\n"); + return -ENOMEM; + } + priv_dev->udev = usbdev; + priv_dev->dev = &intf->dev; + usb_set_intfdata(intf, priv_dev); + + ////// Interface privs + for (inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) { + netdev = alloc_candev(sizeof(struct panda_inf_priv), PANDA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + goto cleanup_candev; + } + netdev->netdev_ops = &panda_netdev_ops; + netdev->flags |= IFF_ECHO; /* we support local echo */ + + priv_inf = netdev_priv(netdev); + priv_inf->netdev = netdev; + priv_inf->priv_dev = priv_dev; + priv_inf->interface_num = inf_num; + priv_inf->mcu_can_ifnum = can_numbering[inf_num]; + + init_usb_anchor(&priv_dev->rx_submitted); + init_usb_anchor(&priv_inf->tx_submitted); + + /* Init CAN device */ + priv_inf->can.state = CAN_STATE_STOPPED; + priv_inf->can.bittiming.bitrate = PANDA_BITRATE; + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register PANDA CAN device: %d\n", err); + free_candev(priv_inf->netdev); + goto cleanup_candev; + } + + priv_dev->interfaces[inf_num] = priv_inf; + } + + err = panda_usb_start(priv_dev); + if (err) { + dev_err(&intf->dev, "Failed to initialize Comma.ai Panda CAN controller\n"); + goto cleanup_candev; + } + + err = panda_set_output_enable(priv_inf, true); + if (err) { + dev_info(&intf->dev, "Failed to initialize send enable message to Panda.\n"); + goto cleanup_candev; + } + + dev_info(&intf->dev, "Comma.ai Panda CAN controller connected\n"); + + return 0; + + cleanup_candev: + for (inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) { + priv_inf = priv_dev->interfaces[inf_num]; + if (priv_inf) { + unregister_candev(priv_inf->netdev); + free_candev(priv_inf->netdev); + } else { + break; + } + } + + kfree(priv_dev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void panda_usb_disconnect(struct usb_interface *intf) +{ + struct panda_dev_priv *priv_dev = usb_get_intfdata(intf); + struct panda_inf_priv *priv_inf; + int inf_num; + + usb_set_intfdata(intf, NULL); + + for (inf_num = 0; inf_num < PANDA_NUM_CAN_INTERFACES; inf_num++) { + priv_inf = priv_dev->interfaces[inf_num]; + if (priv_inf) { + netdev_info(priv_inf->netdev, "device disconnected\n"); + unregister_candev(priv_inf->netdev); + free_candev(priv_inf->netdev); + } else { + break; + } + } + + panda_urb_unlink(priv_inf); + kfree(priv_dev); +} + +static struct usb_driver panda_usb_driver = { + .name = PANDA_MODULE_NAME, + .probe = panda_usb_probe, + .disconnect = panda_usb_disconnect, + .id_table = panda_usb_table, +}; + +module_usb_driver(panda_usb_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jessy Diamond Exum "); +MODULE_DESCRIPTION("SocketCAN driver for Comma.ai's Panda Adapter."); +MODULE_VERSION("0.1"); diff --git a/panda/drivers/linux/test/Makefile b/panda/drivers/linux/test/Makefile new file mode 100644 index 0000000..c73945e --- /dev/null +++ b/panda/drivers/linux/test/Makefile @@ -0,0 +1,2 @@ +all: + gcc main.c -o cantest -pthread -lpthread diff --git a/panda/drivers/linux/test/main.c b/panda/drivers/linux/test/main.c new file mode 100644 index 0000000..1f44efc --- /dev/null +++ b/panda/drivers/linux/test/main.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +const char *ifname = "can0"; + +static unsigned char payload[] = {0xAA, 0xAA, 0xAA, 0xAA, 0x07, 0x00, 0x00, 0x00}; +int packet_len = 8; +int dir = 0; + +void *write_thread( void *dat ){ + int nbytes; + struct can_frame frame; + int s = *((int*) dat); + + while(1){ + for(int i = 0; i < 1; i ++){ + if(packet_len % 2){ + frame.can_id = 0x8AA | CAN_EFF_FLAG; + }else{ + frame.can_id = 0xAA; + } + + frame.can_dlc = packet_len; + memcpy(frame.data, payload, frame.can_dlc); + + nbytes = write(s, &frame, sizeof(struct can_frame)); + + printf("Wrote %d bytes; addr: %lx; datlen: %d\n", nbytes, frame.can_id, frame.can_dlc); + + if(dir){ + packet_len++; + if(packet_len >= 8) + dir = 0; + }else{ + packet_len--; + if(packet_len <= 0) + dir = 1; + } + } + sleep(2); + } +} + + +int main(void) +{ + pthread_t sndthread; + int err, s, nbytes; + struct sockaddr_can addr; + struct can_frame frame; + struct ifreq ifr; + + if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("Error while opening socket"); + return -1; + } + + strcpy(ifr.ifr_name, ifname); + ioctl(s, SIOCGIFINDEX, &ifr); + + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + printf("%s at index %d\n", ifname, ifr.ifr_ifindex); + + if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Error in socket bind"); + return -2; + } + + /////// Create Write Thread + + err = pthread_create( &sndthread, NULL, write_thread, (void*) &s); + if(err){ + fprintf(stderr,"Error - pthread_create() return code: %d\n", err); + exit(EXIT_FAILURE); + } + + /////// Listen to socket + while (1) { + struct can_frame framein; + + // Read in a CAN frame + int numBytes = read(s, &framein, CANFD_MTU); + switch (numBytes) { + case CAN_MTU: + if(framein.can_id & 0x80000000) + printf("Received %u byte payload; canid 0x%lx (EXT)\n", + framein.can_dlc, framein.can_id & 0x7FFFFFFF); + else + printf("Received %u byte payload; canid 0x%lx\n", framein.can_dlc, framein.can_id); + break; + case CANFD_MTU: + // TODO: Should make an example for CAN FD + break; + case -1: + // Check the signal value on interrupt + //if (EINTR == errno) + // continue; + + // Delay before continuing + sleep(1); + default: + continue; + } + } + + return 0; +} diff --git a/panda/drivers/linux/test/run.sh b/panda/drivers/linux/test/run.sh new file mode 100644 index 0000000..5301719 --- /dev/null +++ b/panda/drivers/linux/test/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +sudo ifconfig can0 up +make +./cantest diff --git a/panda/drivers/spi/.gitignore b/panda/drivers/spi/.gitignore new file mode 100644 index 0000000..49dc09d --- /dev/null +++ b/panda/drivers/spi/.gitignore @@ -0,0 +1,7 @@ +spidev.c +*.ko +*.cmd +*.mod +*.symvers +*.order +*.mod.c diff --git a/panda/drivers/spi/Makefile b/panda/drivers/spi/Makefile new file mode 100644 index 0000000..9a2a0ab --- /dev/null +++ b/panda/drivers/spi/Makefile @@ -0,0 +1,14 @@ +obj-m += spidev_panda.o + +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +# GCC9 bug, apply kernel patch instead? +# https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0b999ae3614d09d97a1575936bcee884f912b10e +ccflags-y := -Wno-missing-attributes + +default: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean diff --git a/panda/drivers/spi/load.sh b/panda/drivers/spi/load.sh new file mode 100644 index 0000000..b8efdbf --- /dev/null +++ b/panda/drivers/spi/load.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +make -j8 + +sudo su -c "echo spi0.0 > /sys/bus/spi/drivers/spidev/unbind" || true + +sudo dmesg -C + +#sudo rmmod -f spidev_panda +sudo rmmod spidev_panda || true +sudo insmod spidev_panda.ko + +sudo su -c "echo 'file $DIR/spidev_panda.c +p' > /sys/kernel/debug/dynamic_debug/control" +sudo su -c "echo 'file $DIR/spi_panda.h +p' > /sys/kernel/debug/dynamic_debug/control" + +sudo lsmod + +echo "loaded" +ls -la /dev/spi* +sudo chmod 666 /dev/spi* +ipython -c "from panda import Panda; print(Panda.list())" +KERN=1 ipython -c "from panda import Panda; print(Panda.list())" +dmesg diff --git a/panda/drivers/spi/patch b/panda/drivers/spi/patch new file mode 100644 index 0000000..a146303 --- /dev/null +++ b/panda/drivers/spi/patch @@ -0,0 +1,33 @@ +53c53,54 +< #define SPIDEV_MAJOR 153 /* assigned */ +--- +> int SPIDEV_MAJOR = 0; +> //#define SPIDEV_MAJOR 153 /* assigned */ +354a356,358 +> +> #include "spi_panda.h" +> +413,414c417,419 +< retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, +< (__u8 __user *)arg); +--- +> retval = panda_transfer(spidev, spi, arg); +> //retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, +> // (__u8 __user *)arg); +697,698d701 +< { .compatible = "rohm,dh2228fv" }, +< { .compatible = "lineartechnology,ltc2488" }, +831c834 +< .name = "spidev", +--- +> .name = "spidev_panda", +856c859 +< status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops); +--- +> status = register_chrdev(0, "spi", &spidev_fops); +860c863,865 +< spidev_class = class_create(THIS_MODULE, "spidev"); +--- +> SPIDEV_MAJOR = status; +> +> spidev_class = class_create(THIS_MODULE, "spidev_panda"); diff --git a/panda/drivers/spi/pull-src.sh b/panda/drivers/spi/pull-src.sh new file mode 100644 index 0000000..f74b94b --- /dev/null +++ b/panda/drivers/spi/pull-src.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +rm -f spidev.c +wget https://raw.githubusercontent.com/commaai/agnos-kernel-sdm845/master/drivers/spi/spidev.c + +# diff spidev.c spidev_panda.c > patch +# git diff --no-index spidev.c spidev_panda.c +patch -o spidev_panda.c spidev.c -i patch diff --git a/panda/drivers/spi/spi_panda.h b/panda/drivers/spi/spi_panda.h new file mode 100644 index 0000000..c7da681 --- /dev/null +++ b/panda/drivers/spi/spi_panda.h @@ -0,0 +1,160 @@ +#include +#include +#include + +#define SPI_SYNC 0x5AU +#define SPI_HACK 0x79U +#define SPI_DACK 0x85U +#define SPI_NACK 0x1FU +#define SPI_CHECKSUM_START 0xABU + +struct __attribute__((packed)) spi_header { + u8 sync; + u8 endpoint; + uint16_t tx_len; + uint16_t max_rx_len; +}; + +struct spi_panda_transfer { + __u64 rx_buf; + __u64 tx_buf; + __u32 tx_length; + __u32 rx_length_max; + __u32 timeout; + __u8 endpoint; + __u8 expect_disconnect; +}; + +static u8 panda_calc_checksum(u8 *buf, u16 length) { + int i; + u8 checksum = SPI_CHECKSUM_START; + for (i = 0U; i < length; i++) { + checksum ^= buf[i]; + } + return checksum; +} + +static long panda_wait_for_ack(struct spidev_data *spidev, u8 ack_val, u8 length) { + int i; + int ret; + for (i = 0; i < 1000; i++) { + ret = spidev_sync_read(spidev, length); + if (ret < 0) { + return ret; + } + + if (spidev->rx_buffer[0] == ack_val) { + return 0; + } else if (spidev->rx_buffer[0] == SPI_NACK) { + return -2; + } + if (i > 20) usleep_range(10, 20); + } + return -1; +} + +static long panda_transfer_raw(struct spidev_data *spidev, struct spi_device *spi, unsigned long arg) { + u16 rx_len; + long retval = -1; + struct spi_header header; + struct spi_panda_transfer pt; + + struct spi_transfer t = { + .len = 0, + .tx_buf = spidev->tx_buffer, + .rx_buf = spidev->rx_buffer, + .speed_hz = spidev->spi->max_speed_hz, + }; + + struct spi_message m; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + // read struct from user + if (!access_ok(VERIFY_WRITE, arg, sizeof(pt))) { + return -1; + } + if (copy_from_user(&pt, (void __user *)arg, sizeof(pt))) { + return -1; + } + dev_dbg(&spi->dev, "ep: %d, tx len: %d\n", pt.endpoint, pt.tx_length); + + // send header + header.sync = 0x5a; + header.endpoint = pt.endpoint; + header.tx_len = pt.tx_length; + header.max_rx_len = pt.rx_length_max; + memcpy(spidev->tx_buffer, &header, sizeof(header)); + spidev->tx_buffer[sizeof(header)] = panda_calc_checksum(spidev->tx_buffer, sizeof(header)); + + t.len = sizeof(header) + 1; + retval = spidev_sync(spidev, &m); + if (retval < 0) { + dev_dbg(&spi->dev, "spi xfer failed %ld\n", retval); + return retval; + } + + // wait for ACK + retval = panda_wait_for_ack(spidev, SPI_HACK, 1); + if (retval < 0) { + dev_dbg(&spi->dev, "no header ack %ld\n", retval); + return retval; + } + + // send data + dev_dbg(&spi->dev, "sending data\n"); + retval = copy_from_user(spidev->tx_buffer, (const u8 __user *)(uintptr_t)pt.tx_buf, pt.tx_length); + spidev->tx_buffer[pt.tx_length] = panda_calc_checksum(spidev->tx_buffer, pt.tx_length); + t.len = pt.tx_length + 1; + retval = spidev_sync(spidev, &m); + + if (pt.expect_disconnect) { + return 0; + } + + // wait for ACK + retval = panda_wait_for_ack(spidev, SPI_DACK, 3); + if (retval < 0) { + dev_dbg(&spi->dev, "no data ack\n"); + return retval; + } + + // get response + t.rx_buf = spidev->rx_buffer + 3; + rx_len = (spidev->rx_buffer[2] << 8) | (spidev->rx_buffer[1]); + dev_dbg(&spi->dev, "rx len %u\n", rx_len); + if (rx_len > pt.rx_length_max) { + dev_dbg(&spi->dev, "RX len greater than max\n"); + return -1; + } + + // do the read + t.len = rx_len + 1; + retval = spidev_sync(spidev, &m); + if (retval < 0) { + dev_dbg(&spi->dev, "spi xfer failed %ld\n", retval); + return retval; + } + if (panda_calc_checksum(spidev->rx_buffer, 3 + rx_len + 1) != 0) { + dev_dbg(&spi->dev, "bad checksum\n"); + return -1; + } + + retval = copy_to_user((u8 __user *)(uintptr_t)pt.rx_buf, spidev->rx_buffer + 3, rx_len); + + return rx_len; +} + +static long panda_transfer(struct spidev_data *spidev, struct spi_device *spi, unsigned long arg) { + int i; + int ret; + dev_dbg(&spi->dev, "=== XFER start ===\n"); + for (i = 0; i < 20; i++) { + ret = panda_transfer_raw(spidev, spi, arg); + if (ret >= 0) { + break; + } + } + dev_dbg(&spi->dev, "took %d tries\n", i+1); + return ret; +} diff --git a/panda/drivers/spi/spidev_panda.c b/panda/drivers/spi/spidev_panda.c new file mode 100644 index 0000000..f21fe33 --- /dev/null +++ b/panda/drivers/spi/spidev_panda.c @@ -0,0 +1,891 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +/* + * This supports access to SPI devices using normal userspace I/O calls. + * Note that while traditional UNIX/POSIX I/O semantics are half duplex, + * and often mask message boundaries, full SPI support requires full duplex + * transfers. There are several kinds of internal message boundaries to + * handle chipselect management and other protocol options. + * + * SPI has a character major number assigned. We allocate minor numbers + * dynamically using a bitmask. You must use hotplug tools, such as udev + * (or mdev with busybox) to create and destroy the /dev/spidevB.C device + * nodes, since there is no fixed association of minor numbers with any + * particular SPI bus or device. + */ +int SPIDEV_MAJOR = 0; +//#define SPIDEV_MAJOR 153 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +static DECLARE_BITMAP(minors, N_SPI_MINORS); + + +/* Bit masks for spi_device.mode management. Note that incorrect + * settings for some settings can cause *lots* of trouble for other + * devices on a shared bus: + * + * - CS_HIGH ... this device will be active when it shouldn't be + * - 3WIRE ... when active, it won't behave as it should + * - NO_CS ... there will be no explicit message boundaries; this + * is completely incompatible with the shared bus model + * - READY ... transfers may proceed when they shouldn't. + * + * REVISIT should changing those flags be privileged? + */ +#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ + | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ + | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ + | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) + +struct spidev_data { + dev_t devt; + spinlock_t spi_lock; + struct spi_device *spi; + struct list_head device_entry; + + /* TX/RX buffers are NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned users; + u8 *tx_buffer; + u8 *rx_buffer; + u32 speed_hz; +}; + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +static unsigned bufsiz = 4096; +module_param(bufsiz, uint, S_IRUGO); +MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); + +/*-------------------------------------------------------------------------*/ + +static ssize_t +spidev_sync(struct spidev_data *spidev, struct spi_message *message) +{ + DECLARE_COMPLETION_ONSTACK(done); + int status; + struct spi_device *spi; + + spin_lock_irq(&spidev->spi_lock); + spi = spidev->spi; + spin_unlock_irq(&spidev->spi_lock); + + if (spi == NULL) + status = -ESHUTDOWN; + else + status = spi_sync(spi, message); + + if (status == 0) + status = message->actual_length; + + return status; +} + +static inline ssize_t +spidev_sync_write(struct spidev_data *spidev, size_t len) +{ + struct spi_transfer t = { + .tx_buf = spidev->tx_buffer, + .len = len, + .speed_hz = spidev->speed_hz, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spidev_sync(spidev, &m); +} + +static inline ssize_t +spidev_sync_read(struct spidev_data *spidev, size_t len) +{ + struct spi_transfer t = { + .rx_buf = spidev->rx_buffer, + .len = len, + .speed_hz = spidev->speed_hz, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spidev_sync(spidev, &m); +} + +/*-------------------------------------------------------------------------*/ + +/* Read-only message with current device setup */ +static ssize_t +spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + struct spidev_data *spidev; + ssize_t status = 0; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) + return -EMSGSIZE; + + spidev = filp->private_data; + + mutex_lock(&spidev->buf_lock); + status = spidev_sync_read(spidev, count); + if (status > 0) { + unsigned long missing; + + missing = copy_to_user(buf, spidev->rx_buffer, status); + if (missing == status) + status = -EFAULT; + else + status = status - missing; + } + mutex_unlock(&spidev->buf_lock); + + return status; +} + +/* Write-only message with current device setup */ +static ssize_t +spidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct spidev_data *spidev; + ssize_t status = 0; + unsigned long missing; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) + return -EMSGSIZE; + + spidev = filp->private_data; + + mutex_lock(&spidev->buf_lock); + missing = copy_from_user(spidev->tx_buffer, buf, count); + if (missing == 0) + status = spidev_sync_write(spidev, count); + else + status = -EFAULT; + mutex_unlock(&spidev->buf_lock); + + return status; +} + +static int spidev_message(struct spidev_data *spidev, + struct spi_ioc_transfer *u_xfers, unsigned n_xfers) +{ + struct spi_message msg; + struct spi_transfer *k_xfers; + struct spi_transfer *k_tmp; + struct spi_ioc_transfer *u_tmp; + unsigned n, total, tx_total, rx_total; + u8 *tx_buf, *rx_buf; + int status = -EFAULT; + + spi_message_init(&msg); + k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); + if (k_xfers == NULL) + return -ENOMEM; + + /* Construct spi_message, copying any tx data to bounce buffer. + * We walk the array of user-provided transfers, using each one + * to initialize a kernel version of the same transfer. + */ + tx_buf = spidev->tx_buffer; + rx_buf = spidev->rx_buffer; + total = 0; + tx_total = 0; + rx_total = 0; + for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; + n; + n--, k_tmp++, u_tmp++) { + k_tmp->len = u_tmp->len; + + total += k_tmp->len; + /* Since the function returns the total length of transfers + * on success, restrict the total to positive int values to + * avoid the return value looking like an error. Also check + * each transfer length to avoid arithmetic overflow. + */ + if (total > INT_MAX || k_tmp->len > INT_MAX) { + status = -EMSGSIZE; + goto done; + } + + if (u_tmp->rx_buf) { + /* this transfer needs space in RX bounce buffer */ + rx_total += k_tmp->len; + if (rx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } + k_tmp->rx_buf = rx_buf; + if (!access_ok(VERIFY_WRITE, (u8 __user *) + (uintptr_t) u_tmp->rx_buf, + u_tmp->len)) + goto done; + rx_buf += k_tmp->len; + } + if (u_tmp->tx_buf) { + /* this transfer needs space in TX bounce buffer */ + tx_total += k_tmp->len; + if (tx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } + k_tmp->tx_buf = tx_buf; + if (copy_from_user(tx_buf, (const u8 __user *) + (uintptr_t) u_tmp->tx_buf, + u_tmp->len)) + goto done; + tx_buf += k_tmp->len; + } + + k_tmp->cs_change = !!u_tmp->cs_change; + k_tmp->tx_nbits = u_tmp->tx_nbits; + k_tmp->rx_nbits = u_tmp->rx_nbits; + k_tmp->bits_per_word = u_tmp->bits_per_word; + k_tmp->delay_usecs = u_tmp->delay_usecs; + k_tmp->speed_hz = u_tmp->speed_hz; + if (!k_tmp->speed_hz) + k_tmp->speed_hz = spidev->speed_hz; +#ifdef VERBOSE + dev_dbg(&spidev->spi->dev, + " xfer len %u %s%s%s%dbits %u usec %uHz\n", + u_tmp->len, + u_tmp->rx_buf ? "rx " : "", + u_tmp->tx_buf ? "tx " : "", + u_tmp->cs_change ? "cs " : "", + u_tmp->bits_per_word ? : spidev->spi->bits_per_word, + u_tmp->delay_usecs, + u_tmp->speed_hz ? : spidev->spi->max_speed_hz); +#endif + spi_message_add_tail(k_tmp, &msg); + } + + status = spidev_sync(spidev, &msg); + if (status < 0) + goto done; + + /* copy any rx data out of bounce buffer */ + rx_buf = spidev->rx_buffer; + for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { + if (u_tmp->rx_buf) { + if (__copy_to_user((u8 __user *) + (uintptr_t) u_tmp->rx_buf, rx_buf, + u_tmp->len)) { + status = -EFAULT; + goto done; + } + rx_buf += u_tmp->len; + } + } + status = total; + +done: + kfree(k_xfers); + return status; +} + +static struct spi_ioc_transfer * +spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, + unsigned *n_ioc) +{ + struct spi_ioc_transfer *ioc; + u32 tmp; + + /* Check type, command number and direction */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC + || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) + || _IOC_DIR(cmd) != _IOC_WRITE) + return ERR_PTR(-ENOTTY); + + tmp = _IOC_SIZE(cmd); + if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) + return ERR_PTR(-EINVAL); + *n_ioc = tmp / sizeof(struct spi_ioc_transfer); + if (*n_ioc == 0) + return NULL; + + /* copy into scratch area */ + ioc = kmalloc(tmp, GFP_KERNEL); + if (!ioc) + return ERR_PTR(-ENOMEM); + if (__copy_from_user(ioc, u_ioc, tmp)) { + kfree(ioc); + return ERR_PTR(-EFAULT); + } + return ioc; +} + + +#include "spi_panda.h" + +static long +spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + int retval = 0; + struct spidev_data *spidev; + struct spi_device *spi; + u32 tmp; + unsigned n_ioc; + struct spi_ioc_transfer *ioc; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, + (void __user *)arg, _IOC_SIZE(cmd)); + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, + (void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + spidev = filp->private_data; + spin_lock_irq(&spidev->spi_lock); + spi = spi_dev_get(spidev->spi); + spin_unlock_irq(&spidev->spi_lock); + + if (spi == NULL) + return -ESHUTDOWN; + + /* use the buffer lock here for triple duty: + * - prevent I/O (from us) so calling spi_setup() is safe; + * - prevent concurrent SPI_IOC_WR_* from morphing + * data fields while SPI_IOC_RD_* reads them; + * - SPI_IOC_MESSAGE needs the buffer locked "normally". + */ + mutex_lock(&spidev->buf_lock); + + switch (cmd) { + /* read requests */ + case SPI_IOC_RD_MODE: + retval = __put_user(spi->mode & SPI_MODE_MASK, + (__u8 __user *)arg); + break; + case SPI_IOC_RD_MODE32: + retval = __put_user(spi->mode & SPI_MODE_MASK, + (__u32 __user *)arg); + break; + case SPI_IOC_RD_LSB_FIRST: + retval = panda_transfer(spidev, spi, arg); + //retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, + // (__u8 __user *)arg); + break; + case SPI_IOC_RD_BITS_PER_WORD: + retval = __put_user(spi->bits_per_word, (__u8 __user *)arg); + break; + case SPI_IOC_RD_MAX_SPEED_HZ: + retval = __put_user(spidev->speed_hz, (__u32 __user *)arg); + break; + + /* write requests */ + case SPI_IOC_WR_MODE: + case SPI_IOC_WR_MODE32: + if (cmd == SPI_IOC_WR_MODE) + retval = __get_user(tmp, (u8 __user *)arg); + else + retval = __get_user(tmp, (u32 __user *)arg); + if (retval == 0) { + u32 save = spi->mode; + + if (tmp & ~SPI_MODE_MASK) { + retval = -EINVAL; + break; + } + + tmp |= spi->mode & ~SPI_MODE_MASK; + spi->mode = (u16)tmp; + retval = spi_setup(spi); + if (retval < 0) + spi->mode = save; + else + dev_dbg(&spi->dev, "spi mode %x\n", tmp); + } + break; + case SPI_IOC_WR_LSB_FIRST: + retval = __get_user(tmp, (__u8 __user *)arg); + if (retval == 0) { + u32 save = spi->mode; + + if (tmp) + spi->mode |= SPI_LSB_FIRST; + else + spi->mode &= ~SPI_LSB_FIRST; + retval = spi_setup(spi); + if (retval < 0) + spi->mode = save; + else + dev_dbg(&spi->dev, "%csb first\n", + tmp ? 'l' : 'm'); + } + break; + case SPI_IOC_WR_BITS_PER_WORD: + retval = __get_user(tmp, (__u8 __user *)arg); + if (retval == 0) { + u8 save = spi->bits_per_word; + + spi->bits_per_word = tmp; + retval = spi_setup(spi); + if (retval < 0) + spi->bits_per_word = save; + else + dev_dbg(&spi->dev, "%d bits per word\n", tmp); + } + break; + case SPI_IOC_WR_MAX_SPEED_HZ: + retval = __get_user(tmp, (__u32 __user *)arg); + if (retval == 0) { + u32 save = spi->max_speed_hz; + + spi->max_speed_hz = tmp; + retval = spi_setup(spi); + if (retval >= 0) + spidev->speed_hz = tmp; + else + dev_dbg(&spi->dev, "%d Hz (max)\n", tmp); + spi->max_speed_hz = save; + } + break; + + default: + /* segmented and/or full-duplex I/O request */ + /* Check message and copy into scratch area */ + ioc = spidev_get_ioc_message(cmd, + (struct spi_ioc_transfer __user *)arg, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); + break; + } + if (!ioc) + break; /* n_ioc is also 0 */ + + /* translate to spi_message, execute */ + retval = spidev_message(spidev, ioc, n_ioc); + kfree(ioc); + break; + } + + mutex_unlock(&spidev->buf_lock); + spi_dev_put(spi); + return retval; +} + +#ifdef CONFIG_COMPAT +static long +spidev_compat_ioc_message(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct spi_ioc_transfer __user *u_ioc; + int retval = 0; + struct spidev_data *spidev; + struct spi_device *spi; + unsigned n_ioc, n; + struct spi_ioc_transfer *ioc; + + u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg); + if (!access_ok(VERIFY_READ, u_ioc, _IOC_SIZE(cmd))) + return -EFAULT; + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + spidev = filp->private_data; + spin_lock_irq(&spidev->spi_lock); + spi = spi_dev_get(spidev->spi); + spin_unlock_irq(&spidev->spi_lock); + + if (spi == NULL) + return -ESHUTDOWN; + + /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ + mutex_lock(&spidev->buf_lock); + + /* Check message and copy into scratch area */ + ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); + goto done; + } + if (!ioc) + goto done; /* n_ioc is also 0 */ + + /* Convert buffer pointers */ + for (n = 0; n < n_ioc; n++) { + ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf); + ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf); + } + + /* translate to spi_message, execute */ + retval = spidev_message(spidev, ioc, n_ioc); + kfree(ioc); + +done: + mutex_unlock(&spidev->buf_lock); + spi_dev_put(spi); + return retval; +} + +static long +spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC + && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) + && _IOC_DIR(cmd) == _IOC_WRITE) + return spidev_compat_ioc_message(filp, cmd, arg); + + return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define spidev_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int spidev_open(struct inode *inode, struct file *filp) +{ + struct spidev_data *spidev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(spidev, &device_list, device_entry) { + if (spidev->devt == inode->i_rdev) { + status = 0; + break; + } + } + + if (status) { + pr_debug("spidev: nothing for minor %d\n", iminor(inode)); + goto err_find_dev; + } + + if (!spidev->tx_buffer) { + spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!spidev->tx_buffer) { + dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + goto err_find_dev; + } + } + + if (!spidev->rx_buffer) { + spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!spidev->rx_buffer) { + dev_dbg(&spidev->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + goto err_alloc_rx_buf; + } + } + + spidev->users++; + filp->private_data = spidev; + nonseekable_open(inode, filp); + + mutex_unlock(&device_list_lock); + return 0; + +err_alloc_rx_buf: + kfree(spidev->tx_buffer); + spidev->tx_buffer = NULL; +err_find_dev: + mutex_unlock(&device_list_lock); + return status; +} + +static int spidev_release(struct inode *inode, struct file *filp) +{ + struct spidev_data *spidev; + + mutex_lock(&device_list_lock); + spidev = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + spidev->users--; + if (!spidev->users) { + int dofree; + + kfree(spidev->tx_buffer); + spidev->tx_buffer = NULL; + + kfree(spidev->rx_buffer); + spidev->rx_buffer = NULL; + + spin_lock_irq(&spidev->spi_lock); + if (spidev->spi) + spidev->speed_hz = spidev->spi->max_speed_hz; + + /* ... after we unbound from the underlying device? */ + dofree = (spidev->spi == NULL); + spin_unlock_irq(&spidev->spi_lock); + + if (dofree) + kfree(spidev); + } + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct file_operations spidev_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .write = spidev_write, + .read = spidev_read, + .unlocked_ioctl = spidev_ioctl, + .compat_ioctl = spidev_compat_ioctl, + .open = spidev_open, + .release = spidev_release, + .llseek = no_llseek, +}; + +/*-------------------------------------------------------------------------*/ + +/* The main reason to have this class is to make mdev/udev create the + * /dev/spidevB.C character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +static struct class *spidev_class; + +#ifdef CONFIG_OF +static const struct of_device_id spidev_dt_ids[] = { + { .compatible = "commaai,panda" }, + {}, +}; +MODULE_DEVICE_TABLE(of, spidev_dt_ids); +#endif + +#ifdef CONFIG_ACPI + +/* Dummy SPI devices not to be used in production systems */ +#define SPIDEV_ACPI_DUMMY 1 + +static const struct acpi_device_id spidev_acpi_ids[] = { + /* + * The ACPI SPT000* devices are only meant for development and + * testing. Systems used in production should have a proper ACPI + * description of the connected peripheral and they should also use + * a proper driver instead of poking directly to the SPI bus. + */ + { "SPT0001", SPIDEV_ACPI_DUMMY }, + { "SPT0002", SPIDEV_ACPI_DUMMY }, + { "SPT0003", SPIDEV_ACPI_DUMMY }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, spidev_acpi_ids); + +static void spidev_probe_acpi(struct spi_device *spi) +{ + const struct acpi_device_id *id; + + if (!has_acpi_companion(&spi->dev)) + return; + + id = acpi_match_device(spidev_acpi_ids, &spi->dev); + if (WARN_ON(!id)) + return; + + if (id->driver_data == SPIDEV_ACPI_DUMMY) + dev_warn(&spi->dev, "do not use this driver in production systems!\n"); +} +#else +static inline void spidev_probe_acpi(struct spi_device *spi) {} +#endif + +/*-------------------------------------------------------------------------*/ + +static int spidev_probe(struct spi_device *spi) +{ + struct spidev_data *spidev; + int status; + unsigned long minor; + + /* + * spidev should never be referenced in DT without a specific + * compatible string, it is a Linux implementation thing + * rather than a description of the hardware. + */ + if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { + dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); + WARN_ON(spi->dev.of_node && + !of_match_device(spidev_dt_ids, &spi->dev)); + } + + spidev_probe_acpi(spi); + + /* Allocate driver data */ + spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); + if (!spidev) + return -ENOMEM; + + /* Initialize the driver data */ + spidev->spi = spi; + spin_lock_init(&spidev->spi_lock); + mutex_init(&spidev->buf_lock); + + INIT_LIST_HEAD(&spidev->device_entry); + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + spidev->devt = MKDEV(SPIDEV_MAJOR, minor); + dev = device_create(spidev_class, &spi->dev, spidev->devt, + spidev, "spidev%d.%d", + spi->master->bus_num, spi->chip_select); + status = PTR_ERR_OR_ZERO(dev); + } else { + dev_dbg(&spi->dev, "no minor number available!\n"); + status = -ENODEV; + } + if (status == 0) { + set_bit(minor, minors); + list_add(&spidev->device_entry, &device_list); + } + mutex_unlock(&device_list_lock); + + spidev->speed_hz = spi->max_speed_hz; + + if (status == 0) + spi_set_drvdata(spi, spidev); + else + kfree(spidev); + + return status; +} + +static int spidev_remove(struct spi_device *spi) +{ + struct spidev_data *spidev = spi_get_drvdata(spi); + + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&spidev->spi_lock); + spidev->spi = NULL; + spin_unlock_irq(&spidev->spi_lock); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&spidev->device_entry); + device_destroy(spidev_class, spidev->devt); + clear_bit(MINOR(spidev->devt), minors); + if (spidev->users == 0) + kfree(spidev); + mutex_unlock(&device_list_lock); + + return 0; +} + +static struct spi_driver spidev_spi_driver = { + .driver = { + .name = "spidev_panda", + .of_match_table = of_match_ptr(spidev_dt_ids), + .acpi_match_table = ACPI_PTR(spidev_acpi_ids), + }, + .probe = spidev_probe, + .remove = spidev_remove, + + /* NOTE: suspend/resume methods are not necessary here. + * We don't do anything except pass the requests to/from + * the underlying controller. The refrigerator handles + * most issues; the controller driver handles the rest. + */ +}; + +/*-------------------------------------------------------------------------*/ + +static int __init spidev_init(void) +{ + int status; + + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(0, "spi", &spidev_fops); + if (status < 0) + return status; + + SPIDEV_MAJOR = status; + + spidev_class = class_create(THIS_MODULE, "spidev_panda"); + if (IS_ERR(spidev_class)) { + unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); + return PTR_ERR(spidev_class); + } + + status = spi_register_driver(&spidev_spi_driver); + if (status < 0) { + class_destroy(spidev_class); + unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); + } + return status; +} +module_init(spidev_init); + +static void __exit spidev_exit(void) +{ + spi_unregister_driver(&spidev_spi_driver); + class_destroy(spidev_class); + unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name); +} +module_exit(spidev_exit); + +MODULE_AUTHOR("Andrea Paterniani, "); +MODULE_DESCRIPTION("User mode SPI device interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:spidev"); diff --git a/panda/examples/__init__.py b/panda/examples/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panda/examples/can_bit_transition.md b/panda/examples/can_bit_transition.md new file mode 100644 index 0000000..fa0a6c6 --- /dev/null +++ b/panda/examples/can_bit_transition.md @@ -0,0 +1,25 @@ +# How to use can_bit_transition.py to reverse engineer a single bit field + +Let's say our goal is to find a brake pedal signal (caused by your foot pressing the brake pedal vs adaptive cruise control braking). + +The following process will allow you to quickly find bits that are always 0 during a period of time (when you know you were not pressing the brake with your foot) and always 1 in a different period of time (when you know you were pressing the brake with your foot). + +Open up a drive in cabana where you can find a place you used the brake pedal and another place where you did not use the brake pedal (and you can identify when you were on the brake pedal and when you were not). You may want to go out for a drive and put something in front of the camera after you put your foot on the brake and take it away before you take your foot off the brake so you can easily identify exactly when you had your foot on the brake based on the video in cabana. This is critical because this script needs the brake signal to always be high the entire time for one of the time frames. A 10 second time frame worked well for me. + +I found a drive where I knew I was not pressing the brake between timestamp 50.0 thru 65.0 and I was pressing the brake between timestamp 69.0 thru 79.0. Determine what the timestamps are in cabana by plotting any message and putting your mouse over the plot at the location you want to discover the timestamp. The tool tip on mouse hover has the format: timestamp: value + +Now download the log from cabana (Save Log button) and run the script passing in the timestamps +(replace csv file name with cabana log you downloaded and time ranges with your own) +``` +./can_bit_transition.py ./honda_crv_ex_2017_can-1520354796875.csv 50.0-65.0 69.0-79.0 +``` + +The script will output bits that were always low in the first time range and always high in the second time range (and vice versa) +``` +id 17c 0 -> 1 at byte 4 bitmask 1 +id 17c 0 -> 1 at byte 6 bitmask 32 +id 221 1 -> 0 at byte 0 bitmask 4 +id 1be 0 -> 1 at byte 0 bitmask 16 +``` + +Now I go back to cabana and graph the above bits by searching for the message by id, double clicking on the appropriate bit, and then selecting "show plot". I already knew that message id 0x17c is both user brake and adaptive cruise control braking combined, so I plotted one of those signals along side 0x221 and 0x1be. By replaying a drive I could see that 0x221 was not a brake signal (when high at random times that did not correspond to braking). Next I looked at 0x1be and I found it was the brake pedal signal I was looking for (went high whenever I pressed the brake pedal and did not go high when adaptive cruise control was braking). \ No newline at end of file diff --git a/panda/examples/can_bit_transition.py b/panda/examples/can_bit_transition.py new file mode 100644 index 0000000..1e7bad5 --- /dev/null +++ b/panda/examples/can_bit_transition.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +import csv +import sys + +CSV_KEYS = { + "logger": { + "time": "Time", + "message_id": "MessageID", + "data": "Message", + "bus": "Bus" + }, + "cabana": { + "time": "time", + "message_id": "addr", + "data": "data", + "bus": "bus" + } +} + + +class Message(): + """Details about a specific message ID.""" + + def __init__(self, message_id): + self.message_id = message_id + self.ones = [0] * 64 # bit set if 1 is always seen + self.zeros = [0] * 64 # bit set if 0 is always seen + + def printBitDiff(self, other): + """Prints bits that transition from always zero to always 1 and vice versa.""" + for i in range(len(self.ones)): + zero_to_one = other.zeros[i] & self.ones[i] + if zero_to_one: + print('id %s 0 -> 1 at byte %d bitmask %d' % (self.message_id, i, zero_to_one)) + one_to_zero = other.ones[i] & self.zeros[i] + if one_to_zero: + print('id %s 1 -> 0 at byte %d bitmask %d' % (self.message_id, i, one_to_zero)) + + +class Info(): + """A collection of Messages.""" + + def __init__(self): + self.messages = {} # keyed by MessageID + + def load(self, filename, start, end): + """Given a CSV file, adds information about message IDs and their values.""" + with open(filename, newline='') as inp: + reader = csv.DictReader(inp) + dtype = None + for row in reader: + if not len(row): + continue + if dtype is None: + dtype = "logger" if "Bus" in row else "cabana" + + time = float(row[CSV_KEYS[dtype]["time"]]) + bus = int(row[CSV_KEYS[dtype]["bus"]]) + if time < start or bus > 127: + continue + elif time > end: + break + + message_id = row[CSV_KEYS[dtype]["message_id"]] + if message_id.startswith('0x'): + message_id = message_id[2:] # remove leading '0x' + else: + message_id = hex(int(message_id))[2:] # old message IDs are in decimal + message_id = f'{bus}:{message_id}' + + data = row[CSV_KEYS[dtype]["data"]] + if data.startswith('0x'): + data = data[2:] # remove leading '0x' + else: + data = data + new_message = False + if message_id not in self.messages: + self.messages[message_id] = Message(message_id) + new_message = True + message = self.messages[message_id] + bts = bytearray.fromhex(data) + for i in range(len(bts)): + ones = int(bts[i]) + message.ones[i] = ones if new_message else message.ones[i] & ones + # Inverts the data and masks it to a byte to get the zeros as ones. + zeros = (~int(bts[i])) & 0xff + message.zeros[i] = zeros if new_message else message.zeros[i] & zeros + +def PrintUnique(log_file, low_range, high_range): + # find messages with bits that are always low + start, end = list(map(float, low_range.split('-'))) + low = Info() + low.load(log_file, start, end) + # find messages with bits that are always high + start, end = list(map(float, high_range.split('-'))) + high = Info() + high.load(log_file, start, end) + # print messages that go from low to high + found = False + for message_id in sorted(high.messages, key=lambda x: int(x.split(':')[1], 16)): + if message_id in low.messages: + high.messages[message_id].printBitDiff(low.messages[message_id]) + found = True + if not found: + print('No messages that transition from always low to always high found!') + +if __name__ == "__main__": + if len(sys.argv) < 4: + print('Usage:\n%s log.csv - -' % sys.argv[0]) + sys.exit(0) + PrintUnique(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/panda/examples/can_logger.py b/panda/examples/can_logger.py new file mode 100644 index 0000000..aed3ac9 --- /dev/null +++ b/panda/examples/can_logger.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import csv +import time +from panda import Panda + +def can_logger(): + p = Panda() + + try: + outputfile = open('output.csv', 'w') + csvwriter = csv.writer(outputfile) + # Write Header + csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength', 'Time']) + print("Writing csv file output.csv. Press Ctrl-C to exit...\n") + + bus0_msg_cnt = 0 + bus1_msg_cnt = 0 + bus2_msg_cnt = 0 + + start_time = time.time() + while True: + can_recv = p.can_recv() + + for address, _, dat, src in can_recv: + csvwriter.writerow( + [str(src), str(hex(address)), f"0x{dat.hex()}", len(dat), str(time.time() - start_time)]) + + if src == 0: + bus0_msg_cnt += 1 + elif src == 1: + bus1_msg_cnt += 1 + elif src == 2: + bus2_msg_cnt += 1 + + print(f"Message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}", end='\r') + + except KeyboardInterrupt: + print(f"\nNow exiting. Final message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}") + outputfile.close() + +if __name__ == "__main__": + can_logger() diff --git a/panda/examples/can_unique.md b/panda/examples/can_unique.md new file mode 100644 index 0000000..4d8ac46 --- /dev/null +++ b/panda/examples/can_unique.md @@ -0,0 +1,103 @@ +# How to use can_unique.py to reverse engineer a single bit field + +Let's say our goal is to find the CAN message indicating that the driver's door is either open or closed. +The following process is great for simple single-bit messages. +However for frequently changing values, such as RPM or speed, Cabana's graphical plots are probably better to use. + + +First record a few minutes of background CAN messages with all the doors closed and save it in background.csv: +``` +./can_logger.py +mv output.csv background.csv +``` +Then run can_logger.py for a few seconds while performing the action you're interested, such as opening and then closing the +front-left door and save it as door-fl-1.csv +Repeat the process and save it as door-f1-2.csv to have an easy way to confirm any suspicions. + +Now we'll use can_unique.py to look for unique bits: +``` +$ ./can_unique.py door-fl-1.csv background* +id 820 new one at byte 2 bitmask 2 +id 520 new one at byte 3 bitmask 7 +id 520 new zero at byte 3 bitmask 8 +id 520 new one at byte 5 bitmask 6 +id 520 new zero at byte 5 bitmask 9 +id 559 new zero at byte 6 bitmask 4 +id 804 new one at byte 5 bitmask 2 +id 804 new zero at byte 5 bitmask 1 + +$ ./can_unique.py door-fl-2.csv background* +id 672 new one at byte 3 bitmask 3 +id 820 new one at byte 2 bitmask 2 +id 520 new one at byte 3 bitmask 7 +id 520 new zero at byte 3 bitmask 8 +id 520 new one at byte 5 bitmask 6 +id 520 new zero at byte 5 bitmask 9 +id 559 new zero at byte 6 bitmask 4 +``` + +One of these bits hopefully indicates that the driver's door is open. +Let's go through each message ID to figure out which one is correct. +We expect any correct bits to have changed in both runs. +We can rule out 804 because it only occurred in the first run. +We can rule out 672 because it only occurred in the second run. +That leaves us with these message IDs: 820, 520, 559. Let's take a closer look at each one. + +``` +$ fgrep ,559, door-fl-1.csv |head +0,559,00ff0000000024f0 +0,559,00ff000000004464 +0,559,00ff0000000054a9 +0,559,00ff0000000064e3 +0,559,00ff00000000742e +0,559,00ff000000008451 +0,559,00ff00000000949c +0,559,00ff00000000a4d6 +0,559,00ff00000000b41b +0,559,00ff00000000c442 +``` +Message ID 559 looks like an incrementing value, so it's not what we're looking for. + +``` +$ fgrep ,520, door-fl-2.csv +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +0,520,26ff00f8a2890000 +0,520,26ff00f8a1890000 +``` +Message ID 520 oscillates between two values. However I only opened and closed the door once, so this is probably not it. + +``` +$ fgrep ,820, door-fl-1.csv +0,820,44000100a500c802 +0,820,44000100a500c803 +0,820,44000300a500c803 +0,820,44000300a500c802 +0,820,44000300a500c802 +0,820,44000300a500c802 +0,820,44000100a500c802 +0,820,44000100a500c802 +0,820,44000100a500c802 +``` +Message ID 820 looks promising! It starts off at 44000100a500c802 when the door is closed. +When the door is open it goes to 44000300a500c802. +Then when the door is closed again, it goes back to 44000100a500c802. +Let's confirm by looking at the data from our other run: +``` +$ fgrep ,820, door-fl-2.csv +0,820,44000100a500c802 +0,820,44000300a500c802 +0,820,44000100a500c802 +``` +Perfect! We now know that message id 820 at byte 2 bitmask 2 is set if the driver's door is open. +If we repeat the process with the front passenger's door, +then we'll find that message id 820 at byte 2 bitmask 4 is set if the front-right door is open. +This confirms our finding because it's common for similar signals to be near each other. diff --git a/panda/examples/can_unique.py b/panda/examples/can_unique.py new file mode 100644 index 0000000..05977af --- /dev/null +++ b/panda/examples/can_unique.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +# Given an interesting CSV file of CAN messages and a list of background CAN +# messages, print which bits in the interesting file have never appeared +# in the background files. + +# Expects the CSV file to be in one of the following formats: + +# can_logger.py +# Bus,MessageID,Message,MessageLength +# 0,0x292,0x040000001068,6 + +# The old can_logger.py format is also supported: +# Bus,MessageID,Message +# 0,344,c000c00000000000 + +# Cabana "Save Log" format +# time,addr,bus,data +# 240.47911496100002,53,0,0acc0ade0074bf9e + + +import csv +import sys + +class Message(): + """Details about a specific message ID.""" + + def __init__(self, message_id): + self.message_id = message_id + self.data = {} # keyed by hex string encoded message data + self.ones = [0] * 64 # bit set if 1 is seen + self.zeros = [0] * 64 # bit set if 0 has been seen + + def printBitDiff(self, other): + """Prints bits that are set or cleared compared to other background.""" + for i in range(len(self.ones)): + new_ones = ((~other.ones[i]) & 0xff) & self.ones[i] + if new_ones: + print('id %s new one at byte %d bitmask %d' % ( + self.message_id, i, new_ones)) + new_zeros = ((~other.zeros[i]) & 0xff) & self.zeros[i] + if new_zeros: + print('id %s new zero at byte %d bitmask %d' % ( + self.message_id, i, new_zeros)) + + +class Info(): + """A collection of Messages.""" + + def __init__(self): + self.messages = {} # keyed by MessageID + + def load(self, filename): + """Given a CSV file, adds information about message IDs and their values.""" + with open(filename) as inp: + reader = csv.reader(inp) + header = next(reader, None) + if header[0] == 'time': + self.cabana(reader) + else: + self.logger(reader) + + def cabana(self, reader): + for row in reader: + bus = row[2] + message_id = hex(int(row[1]))[2:] + message_id = f'{bus}:{message_id}' + data = row[3] + self.store(message_id, data) + + def logger(self, reader): + for row in reader: + bus = row[0] + if row[1].startswith('0x'): + message_id = row[1][2:] # remove leading '0x' + else: + message_id = hex(int(row[1]))[2:] # old message IDs are in decimal + message_id = f'{bus}:{message_id}' + if row[1].startswith('0x'): + data = row[2][2:] # remove leading '0x' + else: + data = row[2] + self.store(message_id, data) + + def store(self, message_id, data): + if message_id not in self.messages: + self.messages[message_id] = Message(message_id) + message = self.messages[message_id] + if data not in self.messages[message_id].data: + message.data[data] = True + bts = bytearray.fromhex(data) + for i in range(len(bts)): + message.ones[i] = message.ones[i] | int(bts[i]) + # Inverts the data and masks it to a byte to get the zeros as ones. + message.zeros[i] = message.zeros[i] | ((~int(bts[i])) & 0xff) + + +def PrintUnique(interesting_file, background_files): + background = Info() + for background_file in background_files: + background.load(background_file) + interesting = Info() + interesting.load(interesting_file) + for message_id in sorted(interesting.messages): + if message_id not in background.messages: + print('New message_id: %s' % message_id) + else: + interesting.messages[message_id].printBitDiff( + background.messages[message_id]) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print('Usage:\n%s interesting.csv background*.csv' % sys.argv[0]) + sys.exit(0) + PrintUnique(sys.argv[1], sys.argv[2:]) diff --git a/panda/examples/query_fw_versions.py b/panda/examples/query_fw_versions.py index 4f4e3fa..fe70bf9 100755 --- a/panda/examples/query_fw_versions.py +++ b/panda/examples/query_fw_versions.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import argparse -from typing import List, Optional from tqdm import tqdm from panda import Panda from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, InvalidSubAddressError, \ @@ -25,7 +24,7 @@ if __name__ == "__main__": addrs += [0x18da0000 + (i << 8) + 0xf1 for i in range(256)] results = {} - sub_addrs: List[Optional[int]] = [None] + sub_addrs: list[int | None] = [None] if args.sub_addr: if args.sub_addr == "scan": sub_addrs = list(range(0xff + 1)) diff --git a/panda/examples/query_vin_and_stats.py b/panda/examples/query_vin_and_stats.py new file mode 100644 index 0000000..564b5b9 --- /dev/null +++ b/panda/examples/query_vin_and_stats.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +import time +import struct +from panda import Panda +from hexdump import hexdump +from panda.python.isotp import isotp_send, isotp_recv + +# 0x7e0 = Toyota +# 0x18DB33F1 for Honda? + + +def get_current_data_for_pid(pid): + # 01 xx = Show current data + isotp_send(panda, b"\x01" + bytes([pid]), 0x7e0) + return isotp_recv(panda, 0x7e8) + +def get_supported_pids(): + ret = [] + pid = 0 + while 1: + supported = struct.unpack(">I", get_current_data_for_pid(pid)[2:])[0] + for i in range(1 + pid, 0x21 + pid): + if supported & 0x80000000: + ret.append(i) + supported <<= 1 + pid += 0x20 + if pid not in ret: + break + return ret + +if __name__ == "__main__": + panda = Panda() + panda.set_safety_mode(Panda.SAFETY_ELM327) + panda.can_clear(0) + + # 09 02 = Get VIN + isotp_send(panda, b"\x09\x02", 0x7df) + ret = isotp_recv(panda, 0x7e8) + hexdump(ret) + print("VIN: %s" % "".join(map(chr, ret[:2]))) + + # 03 = get DTCS + isotp_send(panda, b"\x03", 0x7e0) + dtcs = isotp_recv(panda, 0x7e8) + print("DTCs:", "".join(map(chr, dtcs[:2]))) + + supported_pids = get_supported_pids() + print("Supported PIDs:", supported_pids) + + while 1: + speed = struct.unpack(">B", get_current_data_for_pid(13)[2:])[0] # kph + rpm = struct.unpack(">H", get_current_data_for_pid(12)[2:])[0] / 4.0 # revs + throttle = struct.unpack(">B", get_current_data_for_pid(17)[2:])[0] / 255.0 * 100 # percent + temp = struct.unpack(">B", get_current_data_for_pid(5)[2:])[0] - 40 # degrees C + load = struct.unpack(">B", get_current_data_for_pid(4)[2:])[0] / 255.0 * 100 # percent + print("%d KPH, %d RPM, %.1f%% Throttle, %d deg C, %.1f%% load" % (speed, rpm, throttle, temp, load)) + time.sleep(0.2) diff --git a/panda/examples/tesla_tester.py b/panda/examples/tesla_tester.py new file mode 100644 index 0000000..966e39d --- /dev/null +++ b/panda/examples/tesla_tester.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +import binascii +from panda import Panda + +def tesla_tester(): + p = Panda() + + body_bus_speed = 125 # Tesla Body busses (B, BF) are 125kbps, rest are 500kbps + body_bus_num = 1 # My TDC to OBD adapter has PT on bus0 BDY on bus1 and CH on bus2 + p.set_can_speed_kbps(body_bus_num, body_bus_speed) + + # Now set the panda from its default of SAFETY_SILENT (read only) to SAFETY_ALLOUTPUT + # Careful, as this will let us send any CAN messages we want (which could be very bad!) + print("Setting Panda to output mode...") + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # BDY 0x248 is the MCU_commands message, which includes folding mirrors, opening the trunk, frunk, setting the cars lock state and more. + # For our test, we will edit the 3rd byte, which is MCU_lockRequest. 0x01 will lock, 0x02 will unlock: + print("Unlocking Tesla...") + p.can_send(0x248, b"\x00\x00\x02\x00\x00\x00\x00\x00", body_bus_num) + + #Or, we can set the first byte, MCU_frontHoodCommand + MCU_liftgateSwitch, to 0x01 to pop the frunk, or 0x04 to open/close the trunk (0x05 should open both) + print("Opening Frunk...") + p.can_send(0x248, b"\x01\x00\x00\x00\x00\x00\x00\x00", body_bus_num) + + #Back to safety... + print("Disabling output on Panda...") + p.set_safety_mode(Panda.SAFETY_SILENT) + + print("Reading VIN from 0x568. This is painfully slow and can take up to 3 minutes (1 minute per message; 3 messages needed for full VIN)...") + + vin = {} + while True: + #Read the VIN + can_recv = p.can_recv() + for address, _, dat, src in can_recv: + if src == body_bus_num: + if address == 1384: # 0x568 is VIN + vin_index = int(binascii.hexlify(dat)[:2]) # first byte is the index, 00, 01, 02 + vin_string = binascii.hexlify(dat)[2:] # rest of the string is the actual VIN data + vin[vin_index] = vin_string.decode("hex") + print("Got VIN index " + str(vin_index) + " data " + vin[vin_index]) + #if we have all 3 parts of the VIN, print it and break out of our while loop + if 0 in vin and 1 in vin and 2 in vin: + print("VIN: " + vin[0] + vin[1] + vin[2][:3]) + break + +if __name__ == "__main__": + tesla_tester() diff --git a/panda/mypy.ini b/panda/mypy.ini new file mode 100644 index 0000000..0b6de30 --- /dev/null +++ b/panda/mypy.ini @@ -0,0 +1,11 @@ +[mypy] +; third-party packages +ignore_missing_imports = True + +; helpful warnings +warn_redundant_casts = True +warn_unreachable = True +warn_unused_ignores = True + +; restrict dynamic typing +warn_return_any = True diff --git a/panda/panda.png b/panda/panda.png new file mode 100644 index 0000000000000000000000000000000000000000..e18137d8727c0f7ad0bcb31d33451d82458af908 GIT binary patch literal 72509 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%0A!ivXj?e!~l^}OuOPpy(3@Vl7G!0{p$wW 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 a@&5o8I28gBD?g3^0000rzidZ literal 0 HcmV?d00001 diff --git a/panda/pyproject.toml b/panda/pyproject.toml new file mode 100644 index 0000000..ee89db2 --- /dev/null +++ b/panda/pyproject.toml @@ -0,0 +1,14 @@ +# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml +[tool.ruff] +line-length = 160 +target-version="py311" + +[tool.ruff.lint] +select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A"] +ignore = ["W292", "E741", "E402", "C408", "ISC003"] +flake8-implicit-str-concat.allow-multiline=false + +[tool.pytest.ini_options] +# FIXME: pytest 8.0.0 now collects all files, stop pytest-cpp from running these +# the `not Base` filter is needed due to a bug in pytest w/ unittest: https://github.com/pytest-dev/pytest/issues/11552 +addopts = "--ignore=test.sh -n auto -k 'not Base'" diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 900e2ad..1827544 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -6,10 +6,8 @@ import usb1 import struct import hashlib import binascii -import datetime import logging from functools import wraps, partial -from typing import Optional from itertools import accumulate from .base import BaseHandle @@ -218,6 +216,7 @@ class Panda: FLAG_TESLA_POWERTRAIN = 1 FLAG_TESLA_LONG_CONTROL = 2 + FLAG_TESLA_RAVEN = 4 FLAG_VOLKSWAGEN_LONG_CONTROL = 1 @@ -238,13 +237,13 @@ class Panda: FLAG_GM_HW_ASCM_LONG = 16 FLAG_GM_NO_CAMERA = 32 FLAG_GM_NO_ACC = 64 - FLAG_GM_PEDAL_LONG = 128 + FLAG_GM_PEDAL_LONG = 128 # TODO: This can be inferred FLAG_GM_GAS_INTERCEPTOR = 256 FLAG_FORD_LONG_CONTROL = 1 FLAG_FORD_CANFD = 2 - def __init__(self, serial: Optional[str] = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500): + def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500): self._connect_serial = serial self._disable_checks = disable_checks @@ -540,7 +539,7 @@ class Panda: if reconnect: self.reconnect() - def recover(self, timeout: Optional[int] = 60, reset: bool = True) -> bool: + def recover(self, timeout: int | None = 60, reset: bool = True) -> bool: dfu_serial = self.get_dfu_serial() if reset: @@ -559,7 +558,7 @@ class Panda: return True @staticmethod - def wait_for_dfu(dfu_serial: Optional[str], timeout: Optional[int] = None) -> bool: + def wait_for_dfu(dfu_serial: str | None, timeout: int | None = None) -> bool: t_start = time.monotonic() dfu_list = PandaDFU.list() while (dfu_serial is None and len(dfu_list) == 0) or (dfu_serial is not None and dfu_serial not in dfu_list): @@ -571,7 +570,7 @@ class Panda: return True @staticmethod - def wait_for_panda(serial: Optional[str], timeout: int) -> bool: + def wait_for_panda(serial: str | None, timeout: int) -> bool: t_start = time.monotonic() serials = Panda.list() while (serial is None and len(serials) == 0) or (serial is not None and serial not in serials): @@ -904,21 +903,6 @@ class Panda: def set_heartbeat_disabled(self): self._handle.controlWrite(Panda.REQUEST_OUT, 0xf8, 0, 0, b'') - # ******************* RTC ******************* - def set_datetime(self, dt): - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa1, int(dt.year), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa2, int(dt.month), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa3, int(dt.day), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa4, int(dt.isoweekday()), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa5, int(dt.hour), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa6, int(dt.minute), 0, b'') - self._handle.controlWrite(Panda.REQUEST_OUT, 0xa7, int(dt.second), 0, b'') - - def get_datetime(self): - dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa0, 0, 0, 8) - a = struct.unpack("HBBBBBB", dat) - return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6]) - # ****************** Timer ***************** def get_microsecond_timer(self): dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa8, 0, 0, 4) diff --git a/panda/python/constants.py b/panda/python/constants.py index fede524..8b09593 100644 --- a/panda/python/constants.py +++ b/panda/python/constants.py @@ -1,6 +1,6 @@ import os import enum -from typing import List, NamedTuple +from typing import NamedTuple BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../") FW_PATH = os.path.join(BASEDIR, "board/obj/") @@ -10,7 +10,7 @@ USBPACKET_MAX_SIZE = 0x40 class McuConfig(NamedTuple): mcu: str mcu_idcode: int - sector_sizes: List[int] + sector_sizes: list[int] sector_count: int # total sector count, used for MCU identification in DFU mode uid_address: int block_size: int diff --git a/panda/python/dfu.py b/panda/python/dfu.py index 01ff037..9beba45 100644 --- a/panda/python/dfu.py +++ b/panda/python/dfu.py @@ -2,7 +2,6 @@ import os import usb1 import struct import binascii -from typing import List, Optional from .base import BaseSTBootloaderHandle from .spi import STBootloaderSPIHandle, PandaSpiException @@ -11,9 +10,9 @@ from .constants import FW_PATH, McuType class PandaDFU: - def __init__(self, dfu_serial: Optional[str]): + def __init__(self, dfu_serial: str | None): # try USB, then SPI - handle: Optional[BaseSTBootloaderHandle] + handle: BaseSTBootloaderHandle | None self._context, handle = PandaDFU.usb_connect(dfu_serial) if handle is None: self._context, handle = PandaDFU.spi_connect(dfu_serial) @@ -38,7 +37,7 @@ class PandaDFU: self._context.close() @staticmethod - def usb_connect(dfu_serial: Optional[str]): + def usb_connect(dfu_serial: str | None): handle = None context = usb1.USBContext() context.open() @@ -56,7 +55,7 @@ class PandaDFU: return context, handle @staticmethod - def spi_connect(dfu_serial: Optional[str]): + def spi_connect(dfu_serial: str | None): handle = None this_dfu_serial = None @@ -72,13 +71,7 @@ class PandaDFU: return None, handle @staticmethod - def list() -> List[str]: - ret = PandaDFU.usb_list() - ret += PandaDFU.spi_list() - return list(set(ret)) - - @staticmethod - def usb_list() -> List[str]: + def usb_list() -> list[str]: dfu_serials = [] try: with usb1.USBContext() as context: @@ -93,7 +86,7 @@ class PandaDFU: return dfu_serials @staticmethod - def spi_list() -> List[str]: + def spi_list() -> list[str]: try: _, h = PandaDFU.spi_connect(None) if h is not None: @@ -134,3 +127,9 @@ class PandaDFU: code = f.read() self.program_bootstub(code) self.reset() + + @staticmethod + def list() -> list[str]: + ret = PandaDFU.usb_list() + ret += PandaDFU.spi_list() + return list(set(ret)) diff --git a/panda/python/isotp.py b/panda/python/isotp.py index 3334deb..d0bef7d 100644 --- a/panda/python/isotp.py +++ b/panda/python/isotp.py @@ -79,10 +79,10 @@ def isotp_send(panda, x, addr, bus=0, recvaddr=None, subaddr=None, rate=None): sends = [] while len(x) > 0: if subaddr: - sends.append(((bytes([subaddr, 0x20 + (idx & 0xF)]) + x[0:6]).ljust(8, b"\x00"))) + sends.append((bytes([subaddr, 0x20 + (idx & 0xF)]) + x[0:6]).ljust(8, b"\x00")) x = x[6:] else: - sends.append(((bytes([0x20 + (idx & 0xF)]) + x[0:7]).ljust(8, b"\x00"))) + sends.append((bytes([0x20 + (idx & 0xF)]) + x[0:7]).ljust(8, b"\x00")) x = x[7:] idx += 1 diff --git a/panda/python/serial.py b/panda/python/serial.py index 9ac5886..c2e965b 100644 --- a/panda/python/serial.py +++ b/panda/python/serial.py @@ -1,5 +1,5 @@ # mimic a python serial port -class PandaSerial(object): +class PandaSerial: def __init__(self, panda, port, baud): self.panda = panda self.port = port diff --git a/panda/python/spi.py b/panda/python/spi.py index 48dc84d..d34a61d 100644 --- a/panda/python/spi.py +++ b/panda/python/spi.py @@ -9,7 +9,7 @@ import logging import threading from contextlib import contextmanager from functools import reduce -from typing import Callable, List, Optional +from collections.abc import Callable from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT from .constants import McuType, MCU_TYPE_BY_IDCODE, USBPACKET_MAX_SIZE @@ -341,7 +341,7 @@ class STBootloaderSPIHandle(BaseSTBootloaderHandle): elif data != self.ACK: raise PandaSpiMissingAck - def _cmd_no_retry(self, cmd: int, data: Optional[List[bytes]] = None, read_bytes: int = 0, predata=None) -> bytes: + def _cmd_no_retry(self, cmd: int, data: list[bytes] | None = None, read_bytes: int = 0, predata=None) -> bytes: ret = b"" with self.dev.acquire() as spi: # sync + command @@ -371,7 +371,7 @@ class STBootloaderSPIHandle(BaseSTBootloaderHandle): return bytes(ret) - def _cmd(self, cmd: int, data: Optional[List[bytes]] = None, read_bytes: int = 0, predata=None) -> bytes: + def _cmd(self, cmd: int, data: list[bytes] | None = None, read_bytes: int = 0, predata=None) -> bytes: exc = PandaSpiException() for n in range(MAX_XFER_RETRY_COUNT): try: diff --git a/panda/python/uds.py b/panda/python/uds.py index aaa0697..12b4fcd 100644 --- a/panda/python/uds.py +++ b/panda/python/uds.py @@ -1,7 +1,8 @@ import time import struct from collections import deque -from typing import Callable, NamedTuple, Tuple, List, Deque, Generator, Optional, cast +from typing import NamedTuple, Deque, cast +from collections.abc import Callable, Generator from enum import IntEnum from functools import partial @@ -300,8 +301,8 @@ def get_dtc_status_names(status): return result class CanClient(): - def __init__(self, can_send: Callable[[int, bytes, int], None], can_recv: Callable[[], List[Tuple[int, int, bytes, int]]], - tx_addr: int, rx_addr: int, bus: int, sub_addr: Optional[int] = None, debug: bool = False): + def __init__(self, can_send: Callable[[int, bytes, int], None], can_recv: Callable[[], list[tuple[int, int, bytes, int]]], + tx_addr: int, rx_addr: int, bus: int, sub_addr: int | None = None, debug: bool = False): self.tx = can_send self.rx = can_recv self.tx_addr = tx_addr @@ -335,7 +336,7 @@ class CanClient(): msgs = self.rx() if drain: if self.debug: - print("CAN-RX: drain - {}".format(len(msgs))) + print(f"CAN-RX: drain - {len(msgs)}") self.rx_buff.clear() else: for rx_addr, _, rx_data, rx_bus in msgs or []: @@ -366,7 +367,7 @@ class CanClient(): except IndexError: pass # empty - def send(self, msgs: List[bytes], delay: float = 0) -> None: + def send(self, msgs: list[bytes], delay: float = 0) -> None: for i, msg in enumerate(msgs): if delay and i != 0: if self.debug: @@ -443,7 +444,7 @@ class IsoTpMessage(): if not setup_only: self._can_client.send([msg]) - def recv(self, timeout=None) -> Tuple[Optional[bytes], bool]: + def recv(self, timeout=None) -> tuple[bytes | None, bool]: if timeout is None: timeout = self.timeout @@ -566,11 +567,11 @@ def get_rx_addr_for_tx_addr(tx_addr, rx_offset=0x8): # standard 29 bit response addr (flip last two bytes) return (tx_addr & 0xFFFF0000) + (tx_addr << 8 & 0xFF00) + (tx_addr >> 8 & 0xFF) - raise ValueError("invalid tx_addr: {}".format(tx_addr)) + raise ValueError(f"invalid tx_addr: {tx_addr}") class UdsClient(): - def __init__(self, panda, tx_addr: int, rx_addr: Optional[int] = None, bus: int = 0, sub_addr: Optional[int] = None, timeout: float = 1, + def __init__(self, panda, tx_addr: int, rx_addr: int | None = None, bus: int = 0, sub_addr: int | None = None, timeout: float = 1, debug: bool = False, tx_timeout: float = 1, response_pending_timeout: float = 10): self.bus = bus self.tx_addr = tx_addr @@ -583,7 +584,7 @@ class UdsClient(): self.response_pending_timeout = response_pending_timeout # generic uds request - def _uds_request(self, service_type: SERVICE_TYPE, subfunction: Optional[int] = None, data: Optional[bytes] = None) -> bytes: + def _uds_request(self, service_type: SERVICE_TYPE, subfunction: int | None = None, data: bytes | None = None) -> bytes: req = bytes([service_type]) if subfunction is not None: req += bytes([subfunction]) @@ -623,12 +624,12 @@ class UdsClient(): if self.debug: print("UDS-RX: response pending") continue - raise NegativeResponseError('{} - {}'.format(service_desc, error_desc), service_id, error_code) + raise NegativeResponseError(f'{service_desc} - {error_desc}', service_id, error_code) # positive response if service_type + 0x40 != resp_sid: resp_sid_hex = hex(resp_sid) if resp_sid is not None else None - raise InvalidServiceIdError('invalid response service id: {}'.format(resp_sid_hex)) + raise InvalidServiceIdError(f'invalid response service id: {resp_sid_hex}') if subfunction is not None: resp_sfn = resp[1] if len(resp) > 1 else None @@ -671,7 +672,7 @@ class UdsClient(): def tester_present(self, ): self._uds_request(SERVICE_TYPE.TESTER_PRESENT, subfunction=0x00) - def access_timing_parameter(self, timing_parameter_type: TIMING_PARAMETER_TYPE, parameter_values: Optional[bytes] = None): + def access_timing_parameter(self, timing_parameter_type: TIMING_PARAMETER_TYPE, parameter_values: bytes | None = None): write_custom_values = timing_parameter_type == TIMING_PARAMETER_TYPE.SET_TO_GIVEN_VALUES read_values = (timing_parameter_type == TIMING_PARAMETER_TYPE.READ_CURRENTLY_ACTIVE or timing_parameter_type == TIMING_PARAMETER_TYPE.READ_EXTENDED_SET) @@ -714,8 +715,8 @@ class UdsClient(): "data": resp[2:], # TODO: parse the reset of response } - def link_control(self, link_control_type: LINK_CONTROL_TYPE, baud_rate_type: Optional[BAUD_RATE_TYPE] = None): - data: Optional[bytes] + def link_control(self, link_control_type: LINK_CONTROL_TYPE, baud_rate_type: BAUD_RATE_TYPE | None = None): + data: bytes | None if link_control_type == LINK_CONTROL_TYPE.VERIFY_BAUDRATE_TRANSITION_WITH_FIXED_BAUDRATE: # baud_rate_type = BAUD_RATE_TYPE @@ -733,21 +734,21 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.READ_DATA_BY_IDENTIFIER, subfunction=None, data=data) resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None if resp_id != data_identifier_type: - raise ValueError('invalid response data identifier: {} expected: {}'.format(hex(resp_id), hex(data_identifier_type))) + raise ValueError(f'invalid response data identifier: {hex(resp_id)} expected: {hex(data_identifier_type)}') return resp[2:] def read_memory_by_address(self, memory_address: int, memory_size: int, memory_address_bytes: int = 4, memory_size_bytes: int = 1): if memory_address_bytes < 1 or memory_address_bytes > 4: - raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) + raise ValueError(f'invalid memory_address_bytes: {memory_address_bytes}') if memory_size_bytes < 1 or memory_size_bytes > 4: - raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) + raise ValueError(f'invalid memory_size_bytes: {memory_size_bytes}') data = bytes([memory_size_bytes << 4 | memory_address_bytes]) if memory_address >= 1 << (memory_address_bytes * 8): - raise ValueError('invalid memory_address: {}'.format(memory_address)) + raise ValueError(f'invalid memory_address: {memory_address}') data += struct.pack('!I', memory_address)[4 - memory_address_bytes:] if memory_size >= 1 << (memory_size_bytes * 8): - raise ValueError('invalid memory_size: {}'.format(memory_size)) + raise ValueError(f'invalid memory_size: {memory_size}') data += struct.pack('!I', memory_size)[4 - memory_size_bytes:] resp = self._uds_request(SERVICE_TYPE.READ_MEMORY_BY_ADDRESS, subfunction=None, data=data) @@ -758,7 +759,7 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.READ_SCALING_DATA_BY_IDENTIFIER, subfunction=None, data=data) resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None if resp_id != data_identifier_type: - raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) + raise ValueError(f'invalid response data identifier: {hex(resp_id)}') return resp[2:] # TODO: parse the response def read_data_by_periodic_identifier(self, transmission_mode_type: TRANSMISSION_MODE_TYPE, periodic_data_identifier: int): @@ -767,11 +768,11 @@ class UdsClient(): self._uds_request(SERVICE_TYPE.READ_DATA_BY_PERIODIC_IDENTIFIER, subfunction=None, data=data) def dynamically_define_data_identifier(self, dynamic_definition_type: DYNAMIC_DEFINITION_TYPE, dynamic_data_identifier: int, - source_definitions: List[DynamicSourceDefinition], memory_address_bytes: int = 4, memory_size_bytes: int = 1): + source_definitions: list[DynamicSourceDefinition], memory_address_bytes: int = 4, memory_size_bytes: int = 1): if memory_address_bytes < 1 or memory_address_bytes > 4: - raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) + raise ValueError(f'invalid memory_address_bytes: {memory_address_bytes}') if memory_size_bytes < 1 or memory_size_bytes > 4: - raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) + raise ValueError(f'invalid memory_size_bytes: {memory_size_bytes}') data = struct.pack('!H', dynamic_data_identifier) if dynamic_definition_type == DYNAMIC_DEFINITION_TYPE.DEFINE_BY_IDENTIFIER: @@ -781,15 +782,15 @@ class UdsClient(): data += bytes([memory_size_bytes << 4 | memory_address_bytes]) for s in source_definitions: if s.memory_address >= 1 << (memory_address_bytes * 8): - raise ValueError('invalid memory_address: {}'.format(s.memory_address)) + raise ValueError(f'invalid memory_address: {s.memory_address}') data += struct.pack('!I', s.memory_address)[4 - memory_address_bytes:] if s.memory_size >= 1 << (memory_size_bytes * 8): - raise ValueError('invalid memory_size: {}'.format(s.memory_size)) + raise ValueError(f'invalid memory_size: {s.memory_size}') data += struct.pack('!I', s.memory_size)[4 - memory_size_bytes:] elif dynamic_definition_type == DYNAMIC_DEFINITION_TYPE.CLEAR_DYNAMICALLY_DEFINED_DATA_IDENTIFIER: pass else: - raise ValueError('invalid dynamic identifier type: {}'.format(hex(dynamic_definition_type))) + raise ValueError(f'invalid dynamic identifier type: {hex(dynamic_definition_type)}') self._uds_request(SERVICE_TYPE.DYNAMICALLY_DEFINE_DATA_IDENTIFIER, subfunction=dynamic_definition_type, data=data) def write_data_by_identifier(self, data_identifier_type: DATA_IDENTIFIER_TYPE, data_record: bytes): @@ -797,20 +798,20 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.WRITE_DATA_BY_IDENTIFIER, subfunction=None, data=data) resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None if resp_id != data_identifier_type: - raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) + raise ValueError(f'invalid response data identifier: {hex(resp_id)}') def write_memory_by_address(self, memory_address: int, memory_size: int, data_record: bytes, memory_address_bytes: int = 4, memory_size_bytes: int = 1): if memory_address_bytes < 1 or memory_address_bytes > 4: - raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) + raise ValueError(f'invalid memory_address_bytes: {memory_address_bytes}') if memory_size_bytes < 1 or memory_size_bytes > 4: - raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) + raise ValueError(f'invalid memory_size_bytes: {memory_size_bytes}') data = bytes([memory_size_bytes << 4 | memory_address_bytes]) if memory_address >= 1 << (memory_address_bytes * 8): - raise ValueError('invalid memory_address: {}'.format(memory_address)) + raise ValueError(f'invalid memory_address: {memory_address}') data += struct.pack('!I', memory_address)[4 - memory_address_bytes:] if memory_size >= 1 << (memory_size_bytes * 8): - raise ValueError('invalid memory_size: {}'.format(memory_size)) + raise ValueError(f'invalid memory_size: {memory_size}') data += struct.pack('!I', memory_size)[4 - memory_size_bytes:] data += data_record @@ -864,7 +865,7 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.INPUT_OUTPUT_CONTROL_BY_IDENTIFIER, subfunction=None, data=data) resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None if resp_id != data_identifier_type: - raise ValueError('invalid response data identifier: {}'.format(hex(resp_id))) + raise ValueError(f'invalid response data identifier: {hex(resp_id)}') return resp[2:] def routine_control(self, routine_control_type: ROUTINE_CONTROL_TYPE, routine_identifier_type: ROUTINE_IDENTIFIER_TYPE, routine_option_record: bytes = b''): @@ -872,23 +873,23 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.ROUTINE_CONTROL, subfunction=routine_control_type, data=data) resp_id = struct.unpack('!H', resp[0:2])[0] if len(resp) >= 2 else None if resp_id != routine_identifier_type: - raise ValueError('invalid response routine identifier: {}'.format(hex(resp_id))) + raise ValueError(f'invalid response routine identifier: {hex(resp_id)}') return resp[2:] def request_download(self, memory_address: int, memory_size: int, memory_address_bytes: int = 4, memory_size_bytes: int = 4, data_format: int = 0x00): data = bytes([data_format]) if memory_address_bytes < 1 or memory_address_bytes > 4: - raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) + raise ValueError(f'invalid memory_address_bytes: {memory_address_bytes}') if memory_size_bytes < 1 or memory_size_bytes > 4: - raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) + raise ValueError(f'invalid memory_size_bytes: {memory_size_bytes}') data += bytes([memory_size_bytes << 4 | memory_address_bytes]) if memory_address >= 1 << (memory_address_bytes * 8): - raise ValueError('invalid memory_address: {}'.format(memory_address)) + raise ValueError(f'invalid memory_address: {memory_address}') data += struct.pack('!I', memory_address)[4 - memory_address_bytes:] if memory_size >= 1 << (memory_size_bytes * 8): - raise ValueError('invalid memory_size: {}'.format(memory_size)) + raise ValueError(f'invalid memory_size: {memory_size}') data += struct.pack('!I', memory_size)[4 - memory_size_bytes:] resp = self._uds_request(SERVICE_TYPE.REQUEST_DOWNLOAD, subfunction=None, data=data) @@ -896,7 +897,7 @@ class UdsClient(): if max_num_bytes_len >= 1 and max_num_bytes_len <= 4: max_num_bytes = struct.unpack('!I', (b"\x00" * (4 - max_num_bytes_len)) + resp[1:max_num_bytes_len + 1])[0] else: - raise ValueError('invalid max_num_bytes_len: {}'.format(max_num_bytes_len)) + raise ValueError(f'invalid max_num_bytes_len: {max_num_bytes_len}') return max_num_bytes # max number of bytes per transfer data request @@ -904,16 +905,16 @@ class UdsClient(): data = bytes([data_format]) if memory_address_bytes < 1 or memory_address_bytes > 4: - raise ValueError('invalid memory_address_bytes: {}'.format(memory_address_bytes)) + raise ValueError(f'invalid memory_address_bytes: {memory_address_bytes}') if memory_size_bytes < 1 or memory_size_bytes > 4: - raise ValueError('invalid memory_size_bytes: {}'.format(memory_size_bytes)) + raise ValueError(f'invalid memory_size_bytes: {memory_size_bytes}') data += bytes([memory_size_bytes << 4 | memory_address_bytes]) if memory_address >= 1 << (memory_address_bytes * 8): - raise ValueError('invalid memory_address: {}'.format(memory_address)) + raise ValueError(f'invalid memory_address: {memory_address}') data += struct.pack('!I', memory_address)[4 - memory_address_bytes:] if memory_size >= 1 << (memory_size_bytes * 8): - raise ValueError('invalid memory_size: {}'.format(memory_size)) + raise ValueError(f'invalid memory_size: {memory_size}') data += struct.pack('!I', memory_size)[4 - memory_size_bytes:] resp = self._uds_request(SERVICE_TYPE.REQUEST_UPLOAD, subfunction=None, data=data) @@ -921,7 +922,7 @@ class UdsClient(): if max_num_bytes_len >= 1 and max_num_bytes_len <= 4: max_num_bytes = struct.unpack('!I', (b"\x00" * (4 - max_num_bytes_len)) + resp[1:max_num_bytes_len + 1])[0] else: - raise ValueError('invalid max_num_bytes_len: {}'.format(max_num_bytes_len)) + raise ValueError(f'invalid max_num_bytes_len: {max_num_bytes_len}') return max_num_bytes # max number of bytes per transfer data request @@ -930,7 +931,7 @@ class UdsClient(): resp = self._uds_request(SERVICE_TYPE.TRANSFER_DATA, subfunction=None, data=data) resp_id = resp[0] if len(resp) > 0 else None if resp_id != block_sequence_count: - raise ValueError('invalid block_sequence_count: {}'.format(resp_id)) + raise ValueError(f'invalid block_sequence_count: {resp_id}') return resp[1:] def request_transfer_exit(self): diff --git a/panda/python/xcp.py b/panda/python/xcp.py new file mode 100644 index 0000000..bb29404 --- /dev/null +++ b/panda/python/xcp.py @@ -0,0 +1,258 @@ +import sys +import time +import struct +from enum import IntEnum + +class COMMAND_CODE(IntEnum): + CONNECT = 0xFF + DISCONNECT = 0xFE + GET_STATUS = 0xFD + SYNCH = 0xFC + GET_COMM_MODE_INFO = 0xFB + GET_ID = 0xFA + SET_REQUEST = 0xF9 + GET_SEED = 0xF8 + UNLOCK = 0xF7 + SET_MTA = 0xF6 + UPLOAD = 0xF5 + SHORT_UPLOAD = 0xF4 + BUILD_CHECKSUM = 0xF3 + TRANSPORT_LAYER_CMD = 0xF2 + USER_CMD = 0xF1 + DOWNLOAD = 0xF0 + DOWNLOAD_NEXT = 0xEF + DOWNLOAD_MAX = 0xEE + SHORT_DOWNLOAD = 0xED + MODIFY_BITS = 0xEC + SET_CAL_PAGE = 0xEB + GET_CAL_PAGE = 0xEA + GET_PAG_PROCESSOR_INFO = 0xE9 + GET_SEGMENT_INFO = 0xE8 + GET_PAGE_INFO = 0xE7 + SET_SEGMENT_MODE = 0xE6 + GET_SEGMENT_MODE = 0xE5 + COPY_CAL_PAGE = 0xE4 + CLEAR_DAQ_LIST = 0xE3 + SET_DAQ_PTR = 0xE2 + WRITE_DAQ = 0xE1 + SET_DAQ_LIST_MODE = 0xE0 + GET_DAQ_LIST_MODE = 0xDF + START_STOP_DAQ_LIST = 0xDE + START_STOP_SYNCH = 0xDD + GET_DAQ_CLOCK = 0xDC + READ_DAQ = 0xDB + GET_DAQ_PROCESSOR_INFO = 0xDA + GET_DAQ_RESOLUTION_INFO = 0xD9 + GET_DAQ_LIST_INFO = 0xD8 + GET_DAQ_EVENT_INFO = 0xD7 + FREE_DAQ = 0xD6 + ALLOC_DAQ = 0xD5 + ALLOC_ODT = 0xD4 + ALLOC_ODT_ENTRY = 0xD3 + PROGRAM_START = 0xD2 + PROGRAM_CLEAR = 0xD1 + PROGRAM = 0xD0 + PROGRAM_RESET = 0xCF + GET_PGM_PROCESSOR_INFO = 0xCE + GET_SECTOR_INFO = 0xCD + PROGRAM_PREPARE = 0xCC + PROGRAM_FORMAT = 0xCB + PROGRAM_NEXT = 0xCA + PROGRAM_MAX = 0xC9 + PROGRAM_VERIFY = 0xC8 + +ERROR_CODES = { + 0x00: "Command processor synchronization", + 0x10: "Command was not executed", + 0x11: "Command rejected because DAQ is running", + 0x12: "Command rejected because PGM is running", + 0x20: "Unknown command or not implemented optional command", + 0x21: "Command syntax invalid", + 0x22: "Command syntax valid but command parameter(s) out of range", + 0x23: "The memory location is write protected", + 0x24: "The memory location is not accessible", + 0x25: "Access denied, Seed & Key is required", + 0x26: "Selected page not available", + 0x27: "Selected page mode not available", + 0x28: "Selected segment not valid", + 0x29: "Sequence error", + 0x2A: "DAQ configuration not valid", + 0x30: "Memory overflow error", + 0x31: "Generic error", + 0x32: "The slave internal program verify routine detects an error", +} + +class CONNECT_MODE(IntEnum): + NORMAL = 0x00, + USER_DEFINED = 0x01, + +class GET_ID_REQUEST_TYPE(IntEnum): + ASCII = 0x00, + ASAM_MC2_FILE = 0x01, + ASAM_MC2_PATH = 0x02, + ASAM_MC2_URL = 0x03, + ASAM_MC2_UPLOAD = 0x04, + # 128-255 user defined + +class CommandTimeoutError(Exception): + pass + +class CommandCounterError(Exception): + pass + +class CommandResponseError(Exception): + def __init__(self, message, return_code): + super().__init__() + self.message = message + self.return_code = return_code + + def __str__(self): + return self.message + +class XcpClient(): + def __init__(self, panda, tx_addr: int, rx_addr: int, bus: int=0, timeout: float=0.1, debug=False, pad=True): + self.tx_addr = tx_addr + self.rx_addr = rx_addr + self.can_bus = bus + self.timeout = timeout + self.debug = debug + self._panda = panda + self._byte_order = ">" + self._max_cto = 8 + self._max_dto = 8 + self.pad = pad + + def _send_cto(self, cmd: int, dat: bytes = b"") -> None: + tx_data = (bytes([cmd]) + dat) + + # Some ECUs don't respond if the packets are not padded to 8 bytes + if self.pad: + tx_data = tx_data.ljust(8, b"\x00") + + if self.debug: + print("CAN-CLEAR: TX") + self._panda.can_clear(self.can_bus) + if self.debug: + print("CAN-CLEAR: RX") + self._panda.can_clear(0xFFFF) + if self.debug: + print(f"CAN-TX: {hex(self.tx_addr)} - 0x{bytes.hex(tx_data)}") + self._panda.can_send(self.tx_addr, tx_data, self.can_bus) + + def _recv_dto(self, timeout: float) -> bytes: + start_time = time.time() + while time.time() - start_time < timeout: + msgs = self._panda.can_recv() or [] + if len(msgs) >= 256: + print("CAN RX buffer overflow!!!", file=sys.stderr) + for rx_addr, _, rx_data, rx_bus in msgs: + if rx_bus == self.can_bus and rx_addr == self.rx_addr: + rx_data = bytes(rx_data) # convert bytearray to bytes + if self.debug: + print(f"CAN-RX: {hex(rx_addr)} - 0x{bytes.hex(rx_data)}") + + pid = rx_data[0] + if pid == 0xFE: + err = rx_data[1] + err_desc = ERROR_CODES.get(err, "unknown error") + dat = rx_data[2:] + raise CommandResponseError(f"{hex(err)} - {err_desc} {dat}", err) + + return bytes(rx_data[1:]) + time.sleep(0.001) + + raise CommandTimeoutError("timeout waiting for response") + + # commands + def connect(self, connect_mode: CONNECT_MODE=CONNECT_MODE.NORMAL) -> dict: + self._send_cto(COMMAND_CODE.CONNECT, bytes([connect_mode])) + resp = self._recv_dto(self.timeout) + assert len(resp) == 7, f"incorrect data length: {len(resp)}" + self._byte_order = ">" if resp[1] & 0x01 else "<" + self._slave_block_mode = resp[1] & 0x40 != 0 + self._max_cto = resp[2] + self._max_dto = struct.unpack(f"{self._byte_order}H", resp[3:5])[0] + return { + "cal_support": resp[0] & 0x01 != 0, + "daq_support": resp[0] & 0x04 != 0, + "stim_support": resp[0] & 0x08 != 0, + "pgm_support": resp[0] & 0x10 != 0, + "byte_order": self._byte_order, + "address_granularity": 2**((resp[1] & 0x06) >> 1), + "slave_block_mode": self._slave_block_mode, + "optional": resp[1] & 0x80 != 0, + "max_cto": self._max_cto, + "max_dto": self._max_dto, + "protocol_version": resp[5], + "transport_version": resp[6], + } + + def disconnect(self) -> None: + self._send_cto(COMMAND_CODE.DISCONNECT) + resp = self._recv_dto(self.timeout) + assert len(resp) == 0, f"incorrect data length: {len(resp)}" + + def get_id(self, req_id_type: GET_ID_REQUEST_TYPE = GET_ID_REQUEST_TYPE.ASCII) -> dict: + if req_id_type > 255: + raise ValueError("request id type must be less than 255") + self._send_cto(COMMAND_CODE.GET_ID, bytes([req_id_type])) + resp = self._recv_dto(self.timeout) + return { + # mode = 0 means MTA was set + # mode = 1 means data is at end (only CAN-FD has space for this) + "mode": resp[0], + "length": struct.unpack(f"{self._byte_order}I", resp[3:7])[0], + "identifier": resp[7:] if self._max_cto > 8 else None + } + + def get_seed(self, mode: int = 0) -> bytes: + if mode > 255: + raise ValueError("mode must be less than 255") + self._send_cto(COMMAND_CODE.GET_SEED, bytes([0, mode])) + + # TODO: add support for longer seeds spread over multiple blocks + ret = self._recv_dto(self.timeout) + length = ret[0] + return ret[1:length+1] + + def unlock(self, key: bytes) -> bytes: + # TODO: add support for longer keys spread over multiple blocks + self._send_cto(COMMAND_CODE.UNLOCK, bytes([len(key)]) + key) + return self._recv_dto(self.timeout) + + def set_mta(self, addr: int, addr_ext: int = 0) -> bytes: + if addr_ext > 255: + raise ValueError("address extension must be less than 256") + # TODO: this looks broken (missing addr extension) + self._send_cto(COMMAND_CODE.SET_MTA, bytes([0x00, 0x00, addr_ext]) + struct.pack(f"{self._byte_order}I", addr)) + return self._recv_dto(self.timeout) + + def upload(self, size: int) -> bytes: + if size > 255: + raise ValueError("size must be less than 256") + if not self._slave_block_mode and size > self._max_dto - 1: + raise ValueError("block mode not supported") + + self._send_cto(COMMAND_CODE.UPLOAD, bytes([size])) + resp = b"" + while len(resp) < size: + resp += self._recv_dto(self.timeout)[:size - len(resp) + 1] + return resp[:size] # trim off bytes with undefined values + + def short_upload(self, size: int, addr_ext: int, addr: int) -> bytes: + if size > 6: + raise ValueError("size must be less than 7") + if addr_ext > 255: + raise ValueError("address extension must be less than 256") + self._send_cto(COMMAND_CODE.SHORT_UPLOAD, bytes([size, 0x00, addr_ext]) + struct.pack(f"{self._byte_order}I", addr)) + return self._recv_dto(self.timeout)[:size] # trim off bytes with undefined values + + def download(self, data: bytes) -> bytes: + size = len(data) + if size > 255: + raise ValueError("size must be less than 256") + if not self._slave_block_mode and size > self._max_dto - 2: + raise ValueError("block mode not supported") + + self._send_cto(COMMAND_CODE.DOWNLOAD, bytes([size]) + data) + return self._recv_dto(self.timeout)[:size] diff --git a/panda/release/.gitignore b/panda/release/.gitignore new file mode 100644 index 0000000..c4c4ffc --- /dev/null +++ b/panda/release/.gitignore @@ -0,0 +1 @@ +*.zip diff --git a/panda/release/make_release.sh b/panda/release/make_release.sh new file mode 100644 index 0000000..5628c49 --- /dev/null +++ b/panda/release/make_release.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +export CERT=/home/batman/xx/pandaextra/certs/release + +if [ ! -f "$CERT" ]; then + echo "No release cert found, cannot build release." + echo "You probably aren't looking to do this anyway." + exit +fi + +export RELEASE=1 +export BUILDER=DEV + +cd $DIR/../board +scons -u -c +rm obj/* +scons -u +cd obj +RELEASE_NAME=$(awk '{print $1}' version) +zip -j ../../release/panda-$RELEASE_NAME.zip version panda.bin.signed bootstub.panda.bin panda_h7.bin.signed bootstub.panda_h7.bin diff --git a/panda/requirements.txt b/panda/requirements.txt index 23f2cf9..5ee8636 100644 --- a/panda/requirements.txt +++ b/panda/requirements.txt @@ -7,11 +7,12 @@ tqdm>=4.14.0 pytest pytest-mock pytest-xdist -pytest-timeouts +pytest-timeout +pytest-randomly parameterized cffi pre-commit scons>=4.4.0 flaky spidev -termcolor +termcolor \ No newline at end of file diff --git a/panda/setup.py b/panda/setup.py index c419056..25ee669 100644 --- a/panda/setup.py +++ b/panda/setup.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - """ Panda CAN Controller Dongle ~~~~~ diff --git a/panda/tests/__init__.py b/panda/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panda/tests/benchmark.py b/panda/tests/benchmark.py new file mode 100644 index 0000000..c2b0c85 --- /dev/null +++ b/panda/tests/benchmark.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +import time +from contextlib import contextmanager + +from panda import Panda, PandaDFU +from panda.tests.hitl.helpers import get_random_can_messages + + +@contextmanager +def print_time(desc): + start = time.perf_counter() + yield + end = time.perf_counter() + print(f"{end - start:.2f}s - {desc}") + + +if __name__ == "__main__": + with print_time("Panda()"): + p = Panda() + + with print_time("PandaDFU.list()"): + PandaDFU.list() + + fxn = [ + 'reset', + 'reconnect', + 'up_to_date', + 'health', + #'flash', + ] + for f in fxn: + with print_time(f"Panda.{f}()"): + getattr(p, f)() + + p.set_can_loopback(True) + + for n in range(6): + msgs = get_random_can_messages(int(10**n)) + with print_time(f"Panda.can_send_many() - {len(msgs)} msgs"): + p.can_send_many(msgs) + + with print_time("Panda.can_recv()"): + m = p.can_recv() diff --git a/panda/tests/black_white_loopback_test.py b/panda/tests/black_white_loopback_test.py new file mode 100644 index 0000000..5b2312b --- /dev/null +++ b/panda/tests/black_white_loopback_test.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 + +# Loopback test between black panda (+ harness and power) and white/grey panda +# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test. +# To be sure, the test should be run with both harness orientations + + +import os +import time +import random +import argparse +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +counter = 0 +nonzero_bus_errors = 0 +zero_bus_errors = 0 +content_errors = 0 + +def run_test(sleep_duration): + global counter + + pandas = Panda.list() + print(pandas) + + # make sure two pandas are connected + if len(pandas) != 2: + raise Exception("Connect white/grey and black panda to run this test!") + + # connect + pandas[0] = Panda(pandas[0]) + pandas[1] = Panda(pandas[1]) + + black_panda = None + other_panda = None + + # find out which one is black + if pandas[0].is_black() and not pandas[1].is_black(): + black_panda = pandas[0] + other_panda = pandas[1] + elif not pandas[0].is_black() and pandas[1].is_black(): + black_panda = pandas[1] + other_panda = pandas[0] + else: + raise Exception("Connect white/grey and black panda to run this test!") + + # disable safety modes + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test health packet + print("black panda health", black_panda.health()) + print("other panda health", other_panda.health()) + + # test black -> other + while True: + test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration) + test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration) + counter += 1 + print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, "Content errors:", content_errors) + + # Toggle relay + black_panda.set_safety_mode(Panda.SAFETY_SILENT) + time.sleep(1) + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + time.sleep(1) + + +def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): + global nonzero_bus_errors, zero_bus_errors, content_errors + + if direction: + print("***************** TESTING (BLACK --> OTHER) *****************") + else: + print("***************** TESTING (OTHER --> BLACK) *****************") + + for send_bus, obd, recv_buses in test_array: + black_panda.send_heartbeat() + other_panda.send_heartbeat() + print("\ntest can: ", send_bus, " OBD: ", obd) + + # set OBD on black panda + black_panda.set_gmlan(True if obd else None) + + # clear and flush + if direction: + black_panda.can_clear(send_bus) + else: + other_panda.can_clear(send_bus) + + for recv_bus in recv_buses: + if direction: + other_panda.can_clear(recv_bus) + else: + black_panda.can_clear(recv_bus) + + black_panda.can_recv() + other_panda.can_recv() + + # send the characters + at = random.randint(1, 2000) + st = get_test_string()[0:8] + if direction: + black_panda.can_send(at, st, send_bus) + else: + other_panda.can_send(at, st, send_bus) + time.sleep(0.1) + + # check for receive + if direction: + _ = black_panda.can_recv() # can echo + cans_loop = other_panda.can_recv() + else: + _ = other_panda.can_recv() # can echo + cans_loop = black_panda.can_recv() + + loop_buses = [] + for loop in cans_loop: + if (loop[0] != at) or (loop[2] != st): + content_errors += 1 + + print(" Loop on bus", str(loop[3])) + loop_buses.append(loop[3]) + if len(cans_loop) == 0: + print(" No loop") + assert not os.getenv("NOASSERT") + + # test loop buses + recv_buses.sort() + loop_buses.sort() + if(recv_buses != loop_buses): + if len(loop_buses) == 0: + zero_bus_errors += 1 + else: + nonzero_bus_errors += 1 + assert not os.getenv("NOASSERT") + else: + print(" TEST PASSED") + + time.sleep(sleep_duration) + print("\n") + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for _ in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/panda/tests/black_white_relay_endurance.py b/panda/tests/black_white_relay_endurance.py new file mode 100644 index 0000000..db19e72 --- /dev/null +++ b/panda/tests/black_white_relay_endurance.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 + +# Loopback test between black panda (+ harness and power) and white/grey panda +# Tests all buses, including OBD CAN, which is on the same bus as CAN0 in this test. +# To be sure, the test should be run with both harness orientations + + +import os +import time +import random +import argparse + +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +counter = 0 +nonzero_bus_errors = 0 +zero_bus_errors = 0 +content_errors = 0 + +def run_test(sleep_duration): + global counter + + pandas = Panda.list() + print(pandas) + + # make sure two pandas are connected + if len(pandas) != 2: + raise Exception("Connect white/grey and black panda to run this test!") + + # connect + pandas[0] = Panda(pandas[0]) + pandas[1] = Panda(pandas[1]) + + black_panda = None + other_panda = None + + # find out which one is black + if pandas[0].is_black() and not pandas[1].is_black(): + black_panda = pandas[0] + other_panda = pandas[1] + elif not pandas[0].is_black() and pandas[1].is_black(): + black_panda = pandas[1] + other_panda = pandas[0] + else: + raise Exception("Connect white/grey and black panda to run this test!") + + # disable safety modes + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test health packet + print("black panda health", black_panda.health()) + print("other panda health", other_panda.health()) + + # test black -> other + start_time = time.time() + temp_start_time = start_time + while True: + test_buses(black_panda, other_panda, True, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (1, True, [0])], sleep_duration) + test_buses(black_panda, other_panda, False, [(0, False, [0]), (1, False, [1]), (2, False, [2]), (0, True, [0, 1])], sleep_duration) + counter += 1 + + runtime = time.time() - start_time + print("Number of cycles:", counter, "Non-zero bus errors:", nonzero_bus_errors, "Zero bus errors:", zero_bus_errors, + "Content errors:", content_errors, "Runtime: ", runtime) + + if (time.time() - temp_start_time) > 3600 * 6: + # Toggle relay + black_panda.set_safety_mode(Panda.SAFETY_SILENT) + time.sleep(1) + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + time.sleep(1) + temp_start_time = time.time() + + +def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): + global nonzero_bus_errors, zero_bus_errors, content_errors + + if direction: + print("***************** TESTING (BLACK --> OTHER) *****************") + else: + print("***************** TESTING (OTHER --> BLACK) *****************") + + for send_bus, obd, recv_buses in test_array: + black_panda.send_heartbeat() + other_panda.send_heartbeat() + print("\ntest can: ", send_bus, " OBD: ", obd) + + # set OBD on black panda + black_panda.set_gmlan(True if obd else None) + + # clear and flush + if direction: + black_panda.can_clear(send_bus) + else: + other_panda.can_clear(send_bus) + + for recv_bus in recv_buses: + if direction: + other_panda.can_clear(recv_bus) + else: + black_panda.can_clear(recv_bus) + + black_panda.can_recv() + other_panda.can_recv() + + # send the characters + at = random.randint(1, 2000) + st = get_test_string()[0:8] + if direction: + black_panda.can_send(at, st, send_bus) + else: + other_panda.can_send(at, st, send_bus) + time.sleep(0.1) + + # check for receive + if direction: + _ = black_panda.can_recv() # cans echo + cans_loop = other_panda.can_recv() + else: + _ = other_panda.can_recv() # cans echo + cans_loop = black_panda.can_recv() + + loop_buses = [] + for loop in cans_loop: + if (loop[0] != at) or (loop[2] != st): + content_errors += 1 + + print(" Loop on bus", str(loop[3])) + loop_buses.append(loop[3]) + if len(cans_loop) == 0: + print(" No loop") + assert os.getenv("NOASSERT") + + # test loop buses + recv_buses.sort() + loop_buses.sort() + if(recv_buses != loop_buses): + if len(loop_buses) == 0: + zero_bus_errors += 1 + else: + nonzero_bus_errors += 1 + assert os.getenv("NOASSERT") + else: + print(" TEST PASSED") + + time.sleep(sleep_duration) + print("\n") + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for _ in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/panda/tests/black_white_relay_test.py b/panda/tests/black_white_relay_test.py new file mode 100644 index 0000000..90b33be --- /dev/null +++ b/panda/tests/black_white_relay_test.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +# Relay test with loopback between black panda (+ harness and power) and white/grey panda +# Tests the relay switching multiple times / second by looking at the buses on which loop occurs. + + +import os +import time +import random +import argparse + +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +counter = 0 +open_errors = 0 +closed_errors = 0 +content_errors = 0 + +def run_test(sleep_duration): + global counter, open_errors, closed_errors + + pandas = Panda.list() + print(pandas) + + # make sure two pandas are connected + if len(pandas) != 2: + raise Exception("Connect white/grey and black panda to run this test!") + + # connect + pandas[0] = Panda(pandas[0]) + pandas[1] = Panda(pandas[1]) + + # find out which one is black + type0 = pandas[0].get_type() + type1 = pandas[1].get_type() + + black_panda = None + other_panda = None + + if type0 == "\x03" and type1 != "\x03": + black_panda = pandas[0] + other_panda = pandas[1] + elif type0 != "\x03" and type1 == "\x03": + black_panda = pandas[1] + other_panda = pandas[0] + else: + raise Exception("Connect white/grey and black panda to run this test!") + + # disable safety modes + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + other_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test health packet + print("black panda health", black_panda.health()) + print("other panda health", other_panda.health()) + + # test black -> other + while True: + # Switch on relay + black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + time.sleep(0.05) + + if not test_buses(black_panda, other_panda, (0, False, [0])): + open_errors += 1 + raise Exception("Open error") + + # Switch off relay + black_panda.set_safety_mode(Panda.SAFETY_SILENT) + time.sleep(0.05) + + if not test_buses(black_panda, other_panda, (0, False, [0, 2])): + closed_errors += 1 + raise Exception("Close error") + + counter += 1 + print("Number of cycles:", counter, "Open errors:", open_errors, "Closed errors:", closed_errors, "Content errors:", content_errors) + +def test_buses(black_panda, other_panda, test_obj): + global content_errors + send_bus, obd, recv_buses = test_obj + + black_panda.send_heartbeat() + other_panda.send_heartbeat() + + # Set OBD on send panda + other_panda.set_gmlan(True if obd else None) + + # clear and flush + other_panda.can_clear(send_bus) + + for recv_bus in recv_buses: + black_panda.can_clear(recv_bus) + + black_panda.can_recv() + other_panda.can_recv() + + # send the characters + at = random.randint(1, 2000) + st = get_test_string()[0:8] + other_panda.can_send(at, st, send_bus) + time.sleep(0.05) + + # check for receive + _ = other_panda.can_recv() # can echo + cans_loop = black_panda.can_recv() + + loop_buses = [] + for loop in cans_loop: + if (loop[0] != at) or (loop[2] != st): + content_errors += 1 + loop_buses.append(loop[3]) + + # test loop buses + recv_buses.sort() + loop_buses.sort() + if(recv_buses != loop_buses): + return False + else: + return True + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for _ in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/panda/tests/bulk_write_test.py b/panda/tests/bulk_write_test.py new file mode 100644 index 0000000..278766a --- /dev/null +++ b/panda/tests/bulk_write_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import os +import time +import threading +from typing import Any + +from panda import Panda + +JUNGLE = "JUNGLE" in os.environ +if JUNGLE: + from panda import PandaJungle + +# The TX buffers on pandas is 0x100 in length. +NUM_MESSAGES_PER_BUS = 10000 + +def flood_tx(panda): + print('Sending!') + msg = b"\xaa" * 4 + packet = [[0xaa, None, msg, 0], [0xaa, None, msg, 1], [0xaa, None, msg, 2]] * NUM_MESSAGES_PER_BUS + panda.can_send_many(packet, timeout=10000) + print(f"Done sending {3*NUM_MESSAGES_PER_BUS} messages!") + +if __name__ == "__main__": + serials = Panda.list() + if JUNGLE: + sender = Panda() + receiver = PandaJungle() + else: + if len(serials) != 2: + raise Exception("Connect two pandas to perform this test!") + sender = Panda(serials[0]) + receiver = Panda(serials[1]) # type: ignore + receiver.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # Start transmisson + threading.Thread(target=flood_tx, args=(sender,)).start() + + # Receive as much as we can in a few second time period + rx: list[Any] = [] + old_len = 0 + start_time = time.time() + while time.time() - start_time < 3 or len(rx) > old_len: + old_len = len(rx) + print(old_len) + rx.extend(receiver.can_recv()) + print(f"Received {len(rx)} messages") diff --git a/panda/tests/can_printer.py b/panda/tests/can_printer.py new file mode 100644 index 0000000..15ce89f --- /dev/null +++ b/panda/tests/can_printer.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +import os +import time +from collections import defaultdict +import binascii + +from panda import Panda + +# fake +def sec_since_boot(): + return time.time() + +def can_printer(): + p = Panda() + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + start = sec_since_boot() + lp = sec_since_boot() + msgs = defaultdict(list) + canbus = int(os.getenv("CAN", "0")) + while True: + can_recv = p.can_recv() + for address, _, dat, src in can_recv: + if src == canbus: + msgs[address].append(dat) + + if sec_since_boot() - lp > 0.1: + dd = chr(27) + "[2J" + dd += "%5.2f\n" % (sec_since_boot() - start) + for k, v in sorted(zip(list(msgs.keys()), [binascii.hexlify(x[-1]) for x in list(msgs.values())], strict=True)): + dd += "%s(%6d) %s\n" % ("%04X(%4d)" % (k, k), len(msgs[k]), v) + print(dd) + lp = sec_since_boot() + +if __name__ == "__main__": + can_printer() diff --git a/panda/tests/canfd/test_canfd.py b/panda/tests/canfd/test_canfd.py new file mode 100644 index 0000000..873bc79 --- /dev/null +++ b/panda/tests/canfd/test_canfd.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +import os +import time +import random +from collections import defaultdict +from panda import Panda, calculate_checksum, DLC_TO_LEN +from panda import PandaJungle +from panda.tests.hitl.helpers import time_many_sends + +H7_HW_TYPES = [Panda.HW_TYPE_RED_PANDA, Panda.HW_TYPE_RED_PANDA_V2] +JUNGLE_SERIAL = os.getenv("JUNGLE") +H7_PANDAS_EXCLUDE = [] # type: ignore +if os.getenv("H7_PANDAS_EXCLUDE"): + H7_PANDAS_EXCLUDE = os.getenv("H7_PANDAS_EXCLUDE").strip().split(" ") # type: ignore + +def panda_reset(): + panda_serials = [] + + panda_jungle = PandaJungle(JUNGLE_SERIAL) + panda_jungle.set_can_silent(True) + panda_jungle.set_panda_power(False) + time.sleep(1) + panda_jungle.set_panda_power(True) + time.sleep(4) + + for serial in Panda.list(): + if serial not in H7_PANDAS_EXCLUDE: + with Panda(serial=serial) as p: + if p.get_type() in H7_HW_TYPES: + p.reset() + panda_serials.append(serial) + + print("test pandas", panda_serials) + assert len(panda_serials) == 2, "Two H7 pandas required" + + return panda_serials + +def panda_init(serial, enable_canfd=False, enable_non_iso=False): + p = Panda(serial=serial) + p.set_power_save(False) + for bus in range(3): + p.set_can_speed_kbps(0, 500) + if enable_canfd: + p.set_can_data_speed_kbps(bus, 2000) + if enable_non_iso: + p.set_canfd_non_iso(bus, True) + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + return p + +def test_canfd_throughput(p, p_recv=None): + two_pandas = p_recv is not None + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + if two_pandas: + p_recv.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + # enable output mode + else: + p.set_can_loopback(True) + + tests = [ + [500, 1000, 2000], # speeds + [93, 87, 78], # saturation thresholds + ] + + for i in range(len(tests[0])): + # set bus 0 data speed to speed + p.set_can_data_speed_kbps(0, tests[0][i]) + if p_recv is not None: + p_recv.set_can_data_speed_kbps(0, tests[0][i]) + time.sleep(0.05) + + comp_kbps = time_many_sends(p, 0, p_recv=p_recv, msg_count=400, two_pandas=two_pandas, msg_len=64) + + # bit count from https://en.wikipedia.org/wiki/CAN_bus + saturation_pct = (comp_kbps / tests[0][i]) * 100.0 + assert saturation_pct > tests[1][i] + assert saturation_pct < 100 + +def canfd_test(p_send, p_recv): + for n in range(100): + sent_msgs = defaultdict(set) + to_send = [] + for _ in range(200): + bus = random.randrange(3) + for dlc in range(len(DLC_TO_LEN)): + address = random.randrange(1, 1<<29) + data = bytearray(random.getrandbits(8) for _ in range(DLC_TO_LEN[dlc])) + if len(data) >= 2: + data[0] = calculate_checksum(data[1:] + bytes(str(address), encoding="utf-8")) + to_send.append([address, 0, data, bus]) + sent_msgs[bus].add((address, bytes(data))) + + p_send.can_send_many(to_send, timeout=0) + + start_time = time.monotonic() + while (time.monotonic() - start_time < 1) and any(len(x) > 0 for x in sent_msgs.values()): + incoming = p_recv.can_recv() + for msg in incoming: + address, _, data, bus = msg + if len(data) >= 2: + assert calculate_checksum(data[1:] + bytes(str(address), encoding="utf-8")) == data[0] + k = (address, bytes(data)) + assert k in sent_msgs[bus], f"message {k} was never sent on bus {bus}" + sent_msgs[bus].discard(k) + + for bus in range(3): + assert not len(sent_msgs[bus]), f"loop {n}: bus {bus} missing {len(sent_msgs[bus])} messages" + +def setup_test(enable_non_iso=False): + panda_serials = panda_reset() + + p_send = panda_init(panda_serials[0], enable_canfd=False, enable_non_iso=enable_non_iso) + p_recv = panda_init(panda_serials[1], enable_canfd=True, enable_non_iso=enable_non_iso) + + # Check that sending panda CAN FD and BRS are turned off + for bus in range(3): + health = p_send.can_health(bus) + assert not health["canfd_enabled"] + assert not health["brs_enabled"] + assert health["canfd_non_iso"] == enable_non_iso + + # Receiving panda sends dummy CAN FD message that should enable CAN FD on sender side + for bus in range(3): + p_recv.can_send(0x200, b"dummymessage", bus) + p_recv.can_recv() + p_send.can_recv() + + # Check if all tested buses on sending panda have swithed to CAN FD with BRS + for bus in range(3): + health = p_send.can_health(bus) + assert health["canfd_enabled"] + assert health["brs_enabled"] + assert health["canfd_non_iso"] == enable_non_iso + + return p_send, p_recv + +def main(): + print("[TEST CAN-FD]") + p_send, p_recv = setup_test() + canfd_test(p_send, p_recv) + + print("[TEST CAN-FD non-ISO]") + p_send, p_recv = setup_test(enable_non_iso=True) + canfd_test(p_send, p_recv) + + print("[TEST CAN-FD THROUGHPUT]") + panda_serials = panda_reset() + p_send = panda_init(panda_serials[0], enable_canfd=True) + p_recv = panda_init(panda_serials[1], enable_canfd=True) + test_canfd_throughput(p_send, p_recv) + +if __name__ == "__main__": + main() diff --git a/panda/tests/check_fw_size.py b/panda/tests/check_fw_size.py new file mode 100644 index 0000000..53681c5 --- /dev/null +++ b/panda/tests/check_fw_size.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +import subprocess +from collections import defaultdict + + +def check_space(file, mcu): + MCUS = { + "H7": { + ".flash": 1024*1024, # FLASH + ".dtcmram": 128*1024, # DTCMRAM + ".itcmram": 64*1024, # ITCMRAM + ".axisram": 320*1024, # AXI SRAM + ".sram12": 32*1024, # SRAM1(16kb) + SRAM2(16kb) + ".sram4": 16*1024, # SRAM4 + ".backup_sram": 4*1024, # SRAM4 + }, + "F4": { + ".flash": 1024*1024, # FLASH + ".dtcmram": 256*1024, # RAM + ".ram_d1": 64*1024, # RAM2 + }, + } + IGNORE_LIST = [ + ".ARM.attributes", + ".comment", + ".debug_line", + ".debug_info", + ".debug_abbrev", + ".debug_aranges", + ".debug_str", + ".debug_ranges", + ".debug_loc", + ".debug_frame", + ".debug_line_str", + ".debug_rnglists", + ".debug_loclists", + ] + FLASH = [ + ".isr_vector", + ".text", + ".rodata", + ".data" + ] + RAM = [ + ".data", + ".bss", + "._user_heap_stack" # _user_heap_stack considered free? + ] + + result = {} + calcs = defaultdict(int) + + output = str(subprocess.check_output(f"arm-none-eabi-size -x --format=sysv {file}", shell=True), 'utf-8') + + for row in output.split('\n'): + pop = False + line = row.split() + if len(line) == 3 and line[0].startswith('.'): + if line[0] in IGNORE_LIST: + continue + result[line[0]] = [line[1], line[2]] + if line[0] in FLASH: + calcs[".flash"] += int(line[1], 16) + pop = True + if line[0] in RAM: + calcs[".dtcmram"] += int(line[1], 16) + pop = True + if pop: + result.pop(line[0]) + + if len(result): + for line in result: + calcs[line] += int(result[line][0], 16) + + print(f"=======SUMMARY FOR {mcu} FILE {file}=======") + for line in calcs: + if line in MCUS[mcu]: + used_percent = (100 - (MCUS[mcu][line] - calcs[line]) / MCUS[mcu][line] * 100) + print(f"SECTION: {line} size: {MCUS[mcu][line]} USED: {calcs[line]}({used_percent:.2f}%) FREE: {MCUS[mcu][line] - calcs[line]}") + else: + print(line, calcs[line]) + print() + + +if __name__ == "__main__": + # red panda + check_space("../board/obj/bootstub.panda_h7.elf", "H7") + check_space("../board/obj/panda_h7.elf", "H7") + # black panda + check_space("../board/obj/bootstub.panda.elf", "F4") + check_space("../board/obj/panda.elf", "F4") + # jungle v1 + check_space("../board/jungle/obj/bootstub.panda_jungle.elf", "F4") + check_space("../board/jungle/obj/panda_jungle.elf", "F4") + # jungle v2 + check_space("../board/jungle/obj/bootstub.panda_jungle_h7.elf", "H7") + check_space("../board/jungle/obj/panda_jungle_h7.elf", "H7") diff --git a/panda/tests/ci_shell.sh b/panda/tests/ci_shell.sh new file mode 100644 index 0000000..92c0f96 --- /dev/null +++ b/panda/tests/ci_shell.sh @@ -0,0 +1,20 @@ +#!/bin/bash -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +OP_ROOT="$DIR/../../" +PANDA_ROOT="$DIR/../" + +if [ -z "$BUILD" ]; then + docker pull docker.io/commaai/panda:latest +else + docker build --cache-from docker.io/commaai/panda:latest -t docker.io/commaai/panda:latest -f $PANDA_ROOT/Dockerfile $PANDA_ROOT +fi + +docker run \ + -it \ + --rm \ + --volume $OP_ROOT:$OP_ROOT \ + --workdir $PWD \ + --env PYTHONPATH=$OP_ROOT \ + docker.io/commaai/panda:latest \ + /bin/bash diff --git a/panda/tests/debug_console.py b/panda/tests/debug_console.py new file mode 100644 index 0000000..8755be1 --- /dev/null +++ b/panda/tests/debug_console.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +import select +import codecs + +from panda import Panda + +setcolor = ["\033[1;32;40m", "\033[1;31;40m"] +unsetcolor = "\033[00m" + +port_number = int(os.getenv("PORT", "0")) +claim = os.getenv("CLAIM") is not None +no_color = os.getenv("NO_COLOR") is not None +no_reconnect = os.getenv("NO_RECONNECT") is not None + +if __name__ == "__main__": + while True: + try: + serials = Panda.list() + if os.getenv("SERIAL"): + serials = [x for x in serials if x == os.getenv("SERIAL")] + + pandas = [Panda(x, claim=claim) for x in serials] + decoders = [codecs.getincrementaldecoder('utf-8')() for _ in pandas] + + if not len(pandas): + print("no pandas found") + if no_reconnect: + sys.exit(0) + time.sleep(1) + continue + + if os.getenv("BAUD") is not None: + for panda in pandas: + panda.set_uart_baud(port_number, int(os.getenv("BAUD"))) # type: ignore + + while True: + for i, panda in enumerate(pandas): + while True: + ret = panda.serial_read(port_number) + if len(ret) > 0: + decoded = decoders[i].decode(ret) + if no_color: + sys.stdout.write(decoded) + else: + sys.stdout.write(setcolor[i] + decoded + unsetcolor) + sys.stdout.flush() + else: + break + if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []): + ln = sys.stdin.readline() + if claim: + panda.serial_write(port_number, ln) + time.sleep(0.01) + except KeyboardInterrupt: + break + except Exception: + print("panda disconnected!") + time.sleep(0.5) diff --git a/panda/tests/development/register_hashmap_spread.py b/panda/tests/development/register_hashmap_spread.py new file mode 100644 index 0000000..3e20e58 --- /dev/null +++ b/panda/tests/development/register_hashmap_spread.py @@ -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() diff --git a/panda/tests/echo.py b/panda/tests/echo.py new file mode 100644 index 0000000..90bf4a8 --- /dev/null +++ b/panda/tests/echo.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +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". +if __name__ == "__main__": + p = Panda() + 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) diff --git a/panda/tests/elm_car_simulator.py b/panda/tests/elm_car_simulator.py new file mode 100644 index 0000000..56e825f --- /dev/null +++ b/panda/tests/elm_car_simulator.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 + +"""Used to Reverse/Test ELM protocol auto detect and OBD message response without a car.""" + +import os +import sys +import struct +import binascii +import time +import threading +from collections import deque + +from panda import Panda + +def lin_checksum(dat): + return sum(dat) % 0x100 + +class ELMCarSimulator(): + def __init__(self, sn, silent=False, can_kbaud=500, + can=True, can11b=True, can29b=True, + lin=True): + self.__p = Panda(sn if sn else Panda.list()[0]) + self.__on = True + self.__stop = False + self.__silent = silent + + self.__lin_timer = None + self.__lin_active = False + self.__lin_enable = lin + self.__lin_monitor_thread = threading.Thread(target=self.__lin_monitor) + + self.__can_multipart_data = None + self.__can_kbaud = can_kbaud + self.__can_extra_noise_msgs = deque() + self.__can_enable = can + self.__can11b = can11b + self.__can29b = can29b + self.__can_monitor_thread = threading.Thread(target=self.__can_monitor) + + @property + def panda(self): + return self.__p + + def stop(self): + if self.__lin_timer: + self.__lin_timer.cancel() + self.__lin_timeout_handler() + + self.__stop = True + + def join(self): + if self.__lin_monitor_thread.is_alive(): + self.__lin_monitor_thread.join() + if self.__can_monitor_thread.is_alive(): + self.__can_monitor_thread.join() + if self.__p: + print("closing handle") + self.__p.close() + + def set_enable(self, on): + self.__on = on + + def start(self): + self.panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + if self.__lin_enable: + self.__lin_monitor_thread.start() + if self.__can_enable: + self.__can_monitor_thread.start() + + ######################### + # CAN related functions # + ######################### + + def __can_monitor(self): + print("STARTING CAN THREAD") + self.panda.set_can_speed_kbps(0, self.__can_kbaud) + self.panda.can_recv() # Toss whatever was already there + + while not self.__stop: + for address, ts, data, src in self.panda.can_recv(): + if self.__on and src == 0 and len(data) == 8 and data[0] >= 2: + if not self.__silent: + print("Processing CAN message", src, hex(address), binascii.hexlify(data)) + self.__can_process_msg(data[1], data[2], address, ts, data, src) + elif not self.__silent: + print("Rejecting CAN message", src, hex(address), binascii.hexlify(data)) + + def can_mode_11b(self): + self.__can11b = True + self.__can29b = False + + def can_mode_29b(self): + self.__can11b = False + self.__can29b = True + + def can_mode_11b_29b(self): + self.__can11b = True + self.__can29b = True + + def change_can_baud(self, kbaud): + self.__can_kbaud = kbaud + self.panda.set_can_speed_kbps(0, self.__can_kbaud) + + def can_add_extra_noise(self, noise_msg, addr=None): + self.__can_extra_noise_msgs.append((addr, noise_msg)) + + def _can_send(self, addr, msg): + if not self.__silent: + print(" CAN Reply (%x)" % addr, binascii.hexlify(msg)) + self.panda.can_send(addr, msg + b'\x00' * (8 - len(msg)), 0) + if self.__can_extra_noise_msgs: + noise = self.__can_extra_noise_msgs.popleft() + self.panda.can_send(noise[0] if noise[0] is not None else addr, + noise[1] + b'\x00' * (8 - len(noise[1])), 0) + + def _can_addr_matches(self, addr): + if self.__can11b and (addr == 0x7DF or (addr & 0x7F8) == 0x7E0): + return True + if self.__can29b and (addr == 0x18db33f1 or (addr & 0x1FFF00FF) == 0x18da00f1): + return True + return False + + def __can_process_msg(self, mode, pid, address, ts, data, src): + if not self.__silent: + print("CAN MSG", binascii.hexlify(data[1:1 + data[0]]), + "Addr:", hex(address), "Mode:", hex(mode)[2:].zfill(2), + "PID:", hex(pid)[2:].zfill(2), "canLen:", len(data), + binascii.hexlify(data)) + + if self._can_addr_matches(address) and len(data) == 8: + outmsg = None + if data[:3] == b'\x30\x00\x00' and len(self.__can_multipart_data): + if not self.__silent: + print("Request for more data") + outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 + msgnum = 1 + while(self.__can_multipart_data): + datalen = min(7, len(self.__can_multipart_data)) + msgpiece = struct.pack("B", 0x20 | msgnum) + self.__can_multipart_data[:datalen] + self._can_send(outaddr, msgpiece) + self.__can_multipart_data = self.__can_multipart_data[7:] + msgnum = (msgnum + 1) % 0x10 + time.sleep(0.01) + + else: + outmsg = self._process_obd(mode, pid) + + if outmsg: + outaddr = 0x7E8 if address == 0x7DF or address == 0x7E0 else 0x18DAF110 + + if len(outmsg) <= 5: + self._can_send(outaddr, + struct.pack("BBB", len(outmsg) + 2, 0x40 | data[1], pid) + outmsg) + else: + first_msg_len = min(3, len(outmsg) % 7) + payload_len = len(outmsg) + 3 + msgpiece = struct.pack("BBBBB", 0x10 | ((payload_len >> 8) & 0xF), + payload_len & 0xFF, + 0x40 | data[1], pid, 1) + outmsg[:first_msg_len] + self._can_send(outaddr, msgpiece) + self.__can_multipart_data = outmsg[first_msg_len:] + + ######################### + # General OBD functions # + ######################### + + def _process_obd(self, mode, pid): + if mode == 0x01: # Mode: Show current data + if pid == 0x00: # List supported things + return b"\xff\xff\xff\xfe" # b"\xBE\x1F\xB8\x10" #Bitfield, random features + elif pid == 0x01: # Monitor Status since DTC cleared + return b"\x00\x00\x00\x00" # Bitfield, random features + elif pid == 0x04: # Calculated engine load + return b"\x2f" + elif pid == 0x05: # Engine coolant temperature + return b"\x3c" + elif pid == 0x0B: # Intake manifold absolute pressure + return b"\x90" + elif pid == 0x0C: # Engine RPM + return b"\x1A\xF8" + elif pid == 0x0D: # Vehicle Speed + return b"\x53" + elif pid == 0x10: # MAF air flow rate + return b"\x01\xA0" + elif pid == 0x11: # Throttle Position + return b"\x90" + elif pid == 0x33: # Absolute Barometric Pressure + return b"\x90" + elif mode == 0x09: # Mode: Request vehicle information + if pid == 0x02: # Show VIN + return b"1D4GP00R55B123456" + if pid == 0xFC: # test long multi message. Ligned up for LIN responses + return b''.join(struct.pack(">BBH", 0xAA, 0xAA, num + 1) for num in range(80)) + if pid == 0xFD: # test long multi message + parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(80)) + return b'\xAA\xAA\xAA' + b''.join(parts) + if pid == 0xFE: # test very long multi message + parts = (b'\xAA\xAA\xAA' + struct.pack(">I", num) for num in range(584)) + return b'\xAA\xAA\xAA' + b''.join(parts) + b'\xAA' + if pid == 0xFF: + return b'\xAA\x00\x00' + \ + b"".join((b'\xAA' * 5) + struct.pack(">H", num + 1) for num in range(584)) + #return b"\xAA"*100#(0xFFF-3) + + +if __name__ == "__main__": + serial = os.getenv("SERIAL") if os.getenv("SERIAL") else None + kbaud = int(os.getenv("CANKBAUD")) if os.getenv("CANKBAUD") else 500 # type: ignore + bitwidth = int(os.getenv("CANBITWIDTH")) if os.getenv("CANBITWIDTH") else 0 # type: ignore + canenable = bool(int(os.getenv("CANENABLE"))) if os.getenv("CANENABLE") else True # type: ignore + linenable = bool(int(os.getenv("LINENABLE"))) if os.getenv("LINENABLE") else True # type: ignore + sim = ELMCarSimulator(serial, can_kbaud=kbaud, can=canenable, lin=linenable) + if(bitwidth == 0): + sim.can_mode_11b_29b() + if(bitwidth == 11): + sim.can_mode_11b() + if(bitwidth == 29): + sim.can_mode_29b() + + import signal + + def signal_handler(signal, frame): + print('\nShutting down simulator') + sim.stop() + sim.join() + sys.exit(0) + + signal.signal(signal.SIGINT, signal_handler) + + sim.start() + + signal.pause() diff --git a/panda/tests/elm_throughput.py b/panda/tests/elm_throughput.py new file mode 100644 index 0000000..983d4a1 --- /dev/null +++ b/panda/tests/elm_throughput.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import socket +import threading +import select + +class Reader(threading.Thread): + def __init__(self, s, *args, **kwargs): + super().__init__(*args, **kwargs) + self._s = s + self.__stop = False + + def stop(self): + self.__stop = True + + def run(self): + while not self.__stop: + s.recv(1000) + +def read_or_fail(s): + ready = select.select([s], [], [], 4) + assert ready[0], "Socket did not receive data within the timeout duration." + return s.recv(1000) + +def send_msg(s, msg): + s.send(msg) + res = b'' + while not res.endswith(">"): + res += read_or_fail(s) + return res + +if __name__ == "__main__": + s = socket.create_connection(("192.168.0.10", 35000)) + send_msg(s, b"ATZ\r") + send_msg(s, b"ATL1\r") + print(send_msg(s, b"ATE0\r")) + print(send_msg(s, b"ATS0\r")) + print(send_msg(s, b"ATSP6\r")) + + print("\nLOOP\n") + + while True: + print(send_msg(s, b"0100\r")) + print(send_msg(s, b"010d\r")) diff --git a/panda/tests/fan/fan_test.py b/panda/tests/fan/fan_test.py new file mode 100644 index 0000000..36a1171 --- /dev/null +++ b/panda/tests/fan/fan_test.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import time + +from panda import Panda + +if __name__ == "__main__": + p = Panda() + power = 0 + while True: + p.set_fan_power(power) + time.sleep(5) + print("Power: ", power, "RPM:", str(p.get_fan_rpm()), "Expected:", int(6500 * power / 100)) + power += 10 + power %= 110 diff --git a/panda/tests/fan/fan_tuning.py b/panda/tests/fan/fan_tuning.py new file mode 100644 index 0000000..2bdfab7 --- /dev/null +++ b/panda/tests/fan/fan_tuning.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +import json +import time +import threading + +from panda import Panda + +def drain_serial(p): + ret = [] + while True: + d = p.serial_read(0) + if len(d) == 0: + break + ret.append(d) + return ret + + +fan_cmd = 0. + +def logger(event): + # requires a build with DEBUG_FAN + with Panda(claim=False) as p, open('/tmp/fan_log', 'w') as f: + power = None + target_rpm = None + stall_count = None + rpm_fast = None + t = time.monotonic() + + drain_serial(p) + while not event.is_set(): + p.set_fan_power(fan_cmd) + + for l in drain_serial(p)[::-1]: + ns = l.decode('utf8').strip().split(' ') + if len(ns) == 4: + target_rpm, rpm_fast, power, stall_count = (int(n, 16) for n in ns) + break + + dat = { + 't': time.monotonic() - t, + 'cmd_power': fan_cmd, + 'pwm_power': power, + 'target_rpm': target_rpm, + 'rpm_fast': rpm_fast, + 'rpm': p.get_fan_rpm(), + 'stall_counter': stall_count, + 'total_stall_count': p.health()['fan_stall_count'], + } + f.write(json.dumps(dat) + '\n') + time.sleep(1/16.) + p.set_fan_power(0) + +def get_overshoot_rpm(p, power): + global fan_cmd + + # make sure the fan is stopped completely + fan_cmd = 0. + while p.get_fan_rpm() > 100: + time.sleep(0.1) + time.sleep(3) + + # set it to 30% power to mimic going onroad + fan_cmd = power + max_rpm = 0 + max_power = 0 + for _ in range(70): + max_rpm = max(max_rpm, p.get_fan_rpm()) + max_power = max(max_power, p.health()['fan_power']) + time.sleep(0.1) + + # tolerate 10% overshoot + expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 + overshoot = (max_rpm / expected_rpm) - 1 + + return overshoot, max_rpm, max_power + + +if __name__ == "__main__": + event = threading.Event() + threading.Thread(target=logger, args=(event, )).start() + + try: + p = Panda() + for power in range(10, 101, 10): + overshoot, max_rpm, max_power = get_overshoot_rpm(p, power) + print(f"Fan power {power}%: overshoot {overshoot:.2%}, Max RPM {max_rpm}, Max power {max_power}%") + finally: + event.set() diff --git a/panda/tests/get_version.py b/panda/tests/get_version.py new file mode 100644 index 0000000..a013812 --- /dev/null +++ b/panda/tests/get_version.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +from panda import Panda + +if __name__ == "__main__": + for p in Panda.list(): + pp = Panda(p) + print(f"{pp.get_serial()[0]}: {pp.get_version()}") diff --git a/panda/tests/gmbitbang/recv.py b/panda/tests/gmbitbang/recv.py new file mode 100644 index 0000000..8dc594d --- /dev/null +++ b/panda/tests/gmbitbang/recv.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +from panda import Panda + +if __name__ == "__main__": + p = Panda() + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_gmlan(bus=2) + #p.can_send(0xaaa, b"\x00\x00", bus=3) + last_add: int | None = None + while True: + ret = p.can_recv() + if len(ret) > 0: + add = ret[0][0] + if last_add is not None and add != last_add + 1: + print("MISS: ", last_add, add) + last_add = add + print(ret) diff --git a/panda/tests/gmbitbang/rigol.py b/panda/tests/gmbitbang/rigol.py new file mode 100644 index 0000000..818df74 --- /dev/null +++ b/panda/tests/gmbitbang/rigol.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# pylint: skip-file +# type: ignore +import numpy as np +import visa +import matplotlib.pyplot as plt + +resources = visa.ResourceManager() +print(resources.list_resources()) + +scope = resources.open_resource('USB0::0x1AB1::0x04CE::DS1ZA184652242::INSTR', timeout=2000, chunk_size=1024000) +print(scope.query('*IDN?').strip()) + +#voltscale = scope.ask_for_values(':CHAN1:SCAL?')[0] +#voltoffset = scope.ask_for_values(":CHAN1:OFFS?")[0] + +#scope.write(":STOP") +scope.write(":WAV:POIN:MODE RAW") +scope.write(":WAV:DATA? CHAN1")[10:] +rawdata = scope.read_raw() +data = np.frombuffer(rawdata, 'B') +print(data.shape) + +s1 = data[0:650] +s2 = data[650:] +s1i = np.argmax(s1 > 100) +s2i = np.argmax(s2 > 100) +s1 = s1[s1i:] +s2 = s2[s2i:] + +plt.plot(s1) +plt.plot(s2) +plt.show() +#data = (data - 130.0 - voltoffset/voltscale*25) / 25 * voltscale + +print(data) diff --git a/panda/tests/gmbitbang/test.py b/panda/tests/gmbitbang/test.py new file mode 100644 index 0000000..b804113 --- /dev/null +++ b/panda/tests/gmbitbang/test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +import time +from panda import Panda + +p1 = Panda('380016000551363338383037') +p2 = Panda('430026000951363338383037') + +# this is a test, no safety +p1.set_safety_mode(Panda.SAFETY_ALLOUTPUT) +p2.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + +# get versions +print(p1.get_version()) +print(p2.get_version()) + +# this sets bus 2 to actually be GMLAN +p2.set_gmlan(bus=2) + +# send w bitbang then without +#iden = 123 +iden = 18000 +#dat = "\x01\x02" +dat = "\x01\x02\x03\x04\x05\x06\x07\x08" +while 1: + iden += 1 + p1.set_gmlan(bus=None) + p1.can_send(iden, dat, bus=3) + #p1.set_gmlan(bus=2) + #p1.can_send(iden, dat, bus=3) + time.sleep(0.01) + print(p2.can_recv()) + #exit(0) diff --git a/panda/tests/gmbitbang/test_one.py b/panda/tests/gmbitbang/test_one.py new file mode 100644 index 0000000..981edc5 --- /dev/null +++ b/panda/tests/gmbitbang/test_one.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +import time +from panda import Panda + +p = Panda() +p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + +# hack anything on bus +p.set_gmlan(bus=2) +time.sleep(0.1) +while len(p.can_recv()) > 0: + print("clearing") + time.sleep(0.1) +print("cleared") +p.set_gmlan(bus=None) + +iden = 18000 +dat = "\x01\x02\x03\x04\x05\x06\x07\x08" +while 1: + iden += 1 + p.can_send(iden, dat, bus=3) + time.sleep(0.01) diff --git a/panda/tests/gmbitbang/test_packer.c b/panda/tests/gmbitbang/test_packer.c new file mode 100644 index 0000000..63c0131 --- /dev/null +++ b/panda/tests/gmbitbang/test_packer.c @@ -0,0 +1,28 @@ +#include +#include + +#define CANPACKET_DATA_SIZE_MAX 8 +#include "../../board/can_definitions.h" + +#include "../../board/drivers/canbitbang.h" + +int main() { + char out[300]; + CANPacket_t to_bang = {0}; + to_bang.addr = 20 << 18; + to_bang.data_len_code = 1; + to_bang.data[0] = 1; + + int len = get_bit_message(out, &to_bang); + printf("T:"); + for (int i = 0; i < len; i++) { + printf("%d", out[i]); + } + printf("\n"); + printf("R:0000010010100000100010000010011110111010100111111111111111"); + printf("\n"); + return 0; +} + + + diff --git a/panda/tests/gmlan_harness_test.py b/panda/tests/gmlan_harness_test.py new file mode 100644 index 0000000..950918c --- /dev/null +++ b/panda/tests/gmlan_harness_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import time + +from panda import Panda + +WHITE_GMLAN_BUS = 3 +OTHER_GMLAN_BUS = 1 + +def set_gmlan(p): + if p.get_type() == Panda.HW_TYPE_WHITE_PANDA: + p.set_gmlan(2) + else: + p.set_obd(True) + +def set_speed_kbps(p, speed): + if p.get_type() == Panda.HW_TYPE_WHITE_PANDA: + p.set_can_speed_kbps(WHITE_GMLAN_BUS, speed) + else: + p.set_can_speed_kbps(OTHER_GMLAN_BUS, speed) + +def send(p, id_, msg): + if p.get_type() == Panda.HW_TYPE_WHITE_PANDA: + p.can_send(id_, msg, WHITE_GMLAN_BUS) + else: + p.can_send(id_, msg, OTHER_GMLAN_BUS) + +if __name__ == "__main__": + pl = Panda.list() + assert(len(pl) == 2) + p0 = Panda(pl[1]) + p1 = Panda(pl[0]) + + p0.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p1.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + print("0: ", p0.get_type()) + print("1: ", p1.get_type()) + + set_gmlan(p0) + set_gmlan(p1) + + p0.can_clear(0xFFFF) + p1.can_clear(0xFFFF) + + try: + loops = 0 + while True: + for speed in [33.3, 83.3]: + set_speed_kbps(p0, speed) + set_speed_kbps(p1, speed) + p0.can_clear(0xFFFF) + p1.can_clear(0xFFFF) + + print(f"Speed: {speed}") + time.sleep(0.1) + + print("Send 1 -> 0") + send(p1, 1, b"1to0:" + bytes(str(loops%100), "utf-8")) + time.sleep(0.05) + rx = list(filter(lambda x: x[3] < 128, p0.can_recv())) + print(rx) + assert(len(rx) == 1) + + print("Send 0 -> 1") + send(p0, 1, b"0to1:" + bytes(str(loops%100), "utf-8")) + time.sleep(0.05) + rx = list(filter(lambda x: x[3] < 128, p1.can_recv())) + print(rx) + assert(len(rx) == 1) + + time.sleep(0.5) + + + loops += 1 + print(f"Completed {loops} loops") + except Exception: + print("Test failed somehow. Did you power the black panda using the GMLAN harness?") diff --git a/panda/tests/health_test.py b/panda/tests/health_test.py new file mode 100644 index 0000000..1195c2d --- /dev/null +++ b/panda/tests/health_test.py @@ -0,0 +1,18 @@ +#!/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/panda/tests/hitl/1_program.py b/panda/tests/hitl/1_program.py new file mode 100644 index 0000000..6a5087f --- /dev/null +++ b/panda/tests/hitl/1_program.py @@ -0,0 +1,103 @@ +import os +import time +import pytest + +from panda import Panda, PandaDFU, McuType, BASEDIR + + +def check_signature(p): + assert not p.bootstub, "Flashed firmware not booting. Stuck in bootstub." + assert p.up_to_date() + + +def test_dfu(p): + app_mcu_type = p.get_mcu_type() + dfu_serial = p.get_dfu_serial() + + p.reset(enter_bootstub=True) + p.reset(enter_bootloader=True) + assert Panda.wait_for_dfu(dfu_serial, timeout=19), "failed to enter DFU" + + dfu = PandaDFU(dfu_serial) + assert dfu.get_mcu_type() == app_mcu_type + + assert dfu_serial in PandaDFU.list() + + dfu._handle.clear_status() + dfu.reset() + p.reconnect() + +# TODO: make more comprehensive bootstub tests and run on a few production ones + current +# TODO: also test release-signed app +@pytest.mark.timeout(30) +def test_known_bootstub(p): + """ + Test that compiled app can work with known production bootstub + """ + known_bootstubs = { + # covers the two cases listed in Panda.connect + McuType.F4: [ + # case A - no bcdDevice or panda type, has to assume F4 + "bootstub_f4_first_dos_production.panda.bin", + + # case B - just bcdDevice + "bootstub_f4_only_bcd.panda.bin", + ], + McuType.H7: ["bootstub.panda_h7.bin"], + } + + for kb in known_bootstubs[p.get_mcu_type()]: + app_ids = (p.get_mcu_type(), p.get_usb_serial()) + assert None not in app_ids + + p.reset(enter_bootstub=True) + p.reset(enter_bootloader=True) + + dfu_serial = p.get_dfu_serial() + assert Panda.wait_for_dfu(dfu_serial, timeout=30) + + dfu = PandaDFU(dfu_serial) + with open(os.path.join(BASEDIR, "tests/hitl/known_bootstub", kb), "rb") as f: + code = f.read() + + dfu.program_bootstub(code) + dfu.reset() + + p.connect(claim=False, wait=True) + + # check for MCU or serial mismatch + with Panda(p._serial, claim=False) as np: + bootstub_ids = (np.get_mcu_type(), np.get_usb_serial()) + assert app_ids == bootstub_ids + + # ensure we can flash app and it jumps to app + p.flash() + check_signature(p) + assert not p.bootstub + +@pytest.mark.timeout(25) +def test_recover(p): + assert p.recover(timeout=30) + check_signature(p) + +@pytest.mark.timeout(25) +def test_flash(p): + # test flash from bootstub + serial = p._serial + assert serial is not None + p.reset(enter_bootstub=True) + p.close() + time.sleep(2) + + with Panda(serial) as np: + assert np.bootstub + assert np._serial == serial + np.flash() + + p.reconnect() + p.reset() + check_signature(p) + + # test flash from app + p.flash() + check_signature(p) diff --git a/panda/tests/hitl/2_health.py b/panda/tests/hitl/2_health.py new file mode 100644 index 0000000..acb993f --- /dev/null +++ b/panda/tests/hitl/2_health.py @@ -0,0 +1,125 @@ +import time +import pytest + +from panda import Panda +from panda import PandaJungle +from panda.tests.hitl.conftest import PandaGroup + + +def test_ignition(p, panda_jungle): + # Set harness orientation to #2, since the ignition line is on the wrong SBU bus :/ + panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_2) + p.reset() + + for ign in (True, False): + panda_jungle.set_ignition(ign) + time.sleep(0.1) + assert p.health()['ignition_line'] == ign + + +@pytest.mark.test_panda_types(PandaGroup.GEN2) +def test_harness_status(p, panda_jungle): + flipped = None + for ignition in [True, False]: + for orientation in [Panda.HARNESS_STATUS_NC, Panda.HARNESS_STATUS_NORMAL, Panda.HARNESS_STATUS_FLIPPED]: + panda_jungle.set_harness_orientation(orientation) + panda_jungle.set_ignition(ignition) + time.sleep(1) + + health = p.health() + detected_orientation = health['car_harness_status'] + print(f"set: {orientation} detected: {detected_orientation}") + + # Orientation + if orientation == Panda.HARNESS_STATUS_NC: + assert detected_orientation == Panda.HARNESS_STATUS_NC + else: + if flipped is None: + flipped = (detected_orientation != orientation) + + if orientation == Panda.HARNESS_STATUS_NORMAL: + assert detected_orientation == (Panda.HARNESS_STATUS_FLIPPED if flipped else Panda.HARNESS_STATUS_NORMAL) + else: + assert detected_orientation == (Panda.HARNESS_STATUS_NORMAL if flipped else Panda.HARNESS_STATUS_FLIPPED) + + # Line ignition + assert health['ignition_line'] == (False if orientation == Panda.HARNESS_STATUS_NC else ignition) + + # SBU voltages + supply_voltage_mV = 1800 if p.get_type() in [Panda.HW_TYPE_TRES, ] else 3300 + + if orientation == Panda.HARNESS_STATUS_NC: + assert health['sbu1_voltage_mV'] > 0.9 * supply_voltage_mV + assert health['sbu2_voltage_mV'] > 0.9 * supply_voltage_mV + else: + relay_line = 'sbu1_voltage_mV' if (detected_orientation == Panda.HARNESS_STATUS_FLIPPED) else 'sbu2_voltage_mV' + ignition_line = 'sbu2_voltage_mV' if (detected_orientation == Panda.HARNESS_STATUS_FLIPPED) else 'sbu1_voltage_mV' + + assert health[relay_line] < 0.1 * supply_voltage_mV + assert health[ignition_line] > health[relay_line] + if ignition: + assert health[ignition_line] < 0.3 * supply_voltage_mV + else: + assert health[ignition_line] > 0.9 * supply_voltage_mV + + + +@pytest.mark.skip_panda_types((Panda.HW_TYPE_DOS, )) +def test_voltage(p): + for _ in range(10): + voltage = p.health()['voltage'] + assert ((voltage > 11000) and (voltage < 13000)) + time.sleep(0.1) + +def test_hw_type(p): + """ + hw type should be same in bootstub as application + """ + + hw_type = p.get_type() + mcu_type = p.get_mcu_type() + assert mcu_type is not None + + app_uid = p.get_uid() + usb_serial = p.get_usb_serial() + assert app_uid == usb_serial + + p.reset(enter_bootstub=True, reconnect=True) + p.close() + time.sleep(3) + with Panda(p.get_usb_serial()) as pp: + assert pp.bootstub + assert pp.get_type() == hw_type, "Bootstub and app hw type mismatch" + assert pp.get_mcu_type() == mcu_type, "Bootstub and app MCU type mismatch" + assert pp.get_uid() == app_uid + +def test_heartbeat(p, panda_jungle): + panda_jungle.set_ignition(True) + # TODO: add more cases here once the tests aren't super slow + p.set_safety_mode(mode=Panda.SAFETY_HYUNDAI, param=Panda.FLAG_HYUNDAI_LONG) + p.send_heartbeat() + assert p.health()['safety_mode'] == Panda.SAFETY_HYUNDAI + assert p.health()['safety_param'] == Panda.FLAG_HYUNDAI_LONG + + # shouldn't do anything once we're in a car safety mode + p.set_heartbeat_disabled() + + time.sleep(6.) + + h = p.health() + assert h['heartbeat_lost'] + assert h['safety_mode'] == Panda.SAFETY_SILENT + assert h['safety_param'] == 0 + assert h['controls_allowed'] == 0 + +def test_microsecond_timer(p): + start_time = p.get_microsecond_timer() + time.sleep(1) + end_time = p.get_microsecond_timer() + + # account for uint32 overflow + if end_time < start_time: + end_time += 2**32 + + time_diff = (end_time - start_time) / 1e6 + assert 0.98 < time_diff < 1.02, f"Timer not running at the correct speed! (got {time_diff:.2f}s instead of 1.0s)" diff --git a/panda/tests/hitl/3_usb_to_can.py b/panda/tests/hitl/3_usb_to_can.py new file mode 100644 index 0000000..9321eb4 --- /dev/null +++ b/panda/tests/hitl/3_usb_to_can.py @@ -0,0 +1,127 @@ +import time +import pytest +from flaky import flaky + +from panda import Panda +from panda.tests.hitl.conftest import SPEED_NORMAL, SPEED_GMLAN, PandaGroup +from panda.tests.hitl.helpers import time_many_sends + +def test_can_loopback(p): + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + + for bus in (0, 1, 2): + # set bus 0 speed to 5000 + p.set_can_speed_kbps(bus, 500) + + # send a message on bus 0 + p.can_send(0x1aa, b"message", bus) + + # confirm receive both on loopback and send receipt + time.sleep(0.05) + r = p.can_recv() + sr = [x for x in r if x[3] == 0x80 | bus] + lb = [x for x in r if x[3] == bus] + assert len(sr) == 1 + assert len(lb) == 1 + + # confirm data is correct + assert 0x1aa == sr[0][0] == lb[0][0] + assert b"message" == sr[0][2] == lb[0][2] + +def test_reliability(p): + MSG_COUNT = 100 + + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + p.set_can_speed_kbps(0, 1000) + + addrs = list(range(100, 100 + MSG_COUNT)) + ts = [(j, 0, b"\xaa" * 8, 0) for j in addrs] + + for _ in range(100): + st = time.monotonic() + + p.can_send_many(ts) + + r = [] + while len(r) < 200 and (time.monotonic() - st) < 0.5: + r.extend(p.can_recv()) + + sent_echo = [x for x in r if x[3] == 0x80] + loopback_resp = [x for x in r if x[3] == 0] + + assert sorted([x[0] for x in loopback_resp]) == addrs + assert sorted([x[0] for x in sent_echo]) == addrs + assert len(r) == 200 + + # take sub 20ms + et = (time.monotonic() - st) * 1000.0 + assert et < 20 + +@flaky(max_runs=6, min_passes=1) +def test_throughput(p): + # enable output mode + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # enable CAN loopback mode + p.set_can_loopback(True) + + for speed in [10, 20, 50, 100, 125, 250, 500, 1000]: + # set bus 0 speed to speed + p.set_can_speed_kbps(0, speed) + time.sleep(0.05) + + comp_kbps = time_many_sends(p, 0) + + # bit count from https://en.wikipedia.org/wiki/CAN_bus + saturation_pct = (comp_kbps / speed) * 100.0 + assert saturation_pct > 80 + assert saturation_pct < 100 + + print("loopback 100 messages at speed %d, comp speed is %.2f, percent %.2f" % (speed, comp_kbps, saturation_pct)) + +@pytest.mark.test_panda_types(PandaGroup.GMLAN) +def test_gmlan(p): + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + + # set gmlan on CAN2 + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(bus) + comp_kbps_gmlan = time_many_sends(p, 3) + assert comp_kbps_gmlan > (0.8 * SPEED_GMLAN) + assert comp_kbps_gmlan < (1.0 * SPEED_GMLAN) + + p.set_gmlan(None) + comp_kbps_normal = time_many_sends(p, bus) + assert comp_kbps_normal > (0.8 * SPEED_NORMAL) + assert comp_kbps_normal < (1.0 * SPEED_NORMAL) + + print("%d: %.2f kbps vs %.2f kbps" % (bus, comp_kbps_gmlan, comp_kbps_normal)) + +@pytest.mark.test_panda_types(PandaGroup.GMLAN) +def test_gmlan_bad_toggle(p): + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + + # GMLAN_CAN2 + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(bus) + comp_kbps_gmlan = time_many_sends(p, 3) + assert comp_kbps_gmlan > (0.6 * SPEED_GMLAN) + assert comp_kbps_gmlan < (1.0 * SPEED_GMLAN) + + # normal + for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: + p.set_gmlan(None) + comp_kbps_normal = time_many_sends(p, bus) + assert comp_kbps_normal > (0.6 * SPEED_NORMAL) + assert comp_kbps_normal < (1.0 * SPEED_NORMAL) + + +# this will fail if you have hardware serial connected +def test_serial_debug(p): + _ = p.serial_read(Panda.SERIAL_DEBUG) # junk + p.call_control_api(0x01) + assert p.serial_read(Panda.SERIAL_DEBUG).startswith(b"NO HANDLER") diff --git a/panda/tests/hitl/4_can_loopback.py b/panda/tests/hitl/4_can_loopback.py new file mode 100644 index 0000000..a7e1aea --- /dev/null +++ b/panda/tests/hitl/4_can_loopback.py @@ -0,0 +1,202 @@ +import os +import time +import pytest +import random +import threading +from flaky import flaky +from collections import defaultdict + +from panda import Panda +from panda.tests.hitl.conftest import PandaGroup +from panda.tests.hitl.helpers import time_many_sends, get_random_can_messages, clear_can_buffers + +@flaky(max_runs=3, min_passes=1) +@pytest.mark.timeout(35) +def test_send_recv(p, panda_jungle): + def test(p_send, p_recv): + for bus in (0, 1, 2): + for speed in (10, 20, 50, 100, 125, 250, 500, 1000): + clear_can_buffers(p_send, speed) + clear_can_buffers(p_recv, speed) + + comp_kbps = time_many_sends(p_send, bus, p_recv, two_pandas=True) + + saturation_pct = (comp_kbps / speed) * 100.0 + assert 80 < saturation_pct < 100 + + print(f"two pandas bus {bus}, 100 messages at speed {speed:4d}, comp speed is {comp_kbps:7.2f}, {saturation_pct:6.2f}%") + + # Run tests in both directions + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + test(p, panda_jungle) + test(panda_jungle, p) + + +@flaky(max_runs=6, min_passes=1) +@pytest.mark.timeout(30) +def test_latency(p, panda_jungle): + def test(p_send, p_recv): + for bus in (0, 1, 2): + for speed in (10, 20, 50, 100, 125, 250, 500, 1000): + clear_can_buffers(p_send, speed) + clear_can_buffers(p_recv, speed) + + latencies = [] + comp_kbps_list = [] + saturation_pcts = [] + + num_messages = 100 + + for _ in range(num_messages): + st = time.monotonic() + p_send.can_send(0x1ab, b"message", bus) + r = [] + while len(r) < 1 and (time.monotonic() - st) < 5: + r = p_recv.can_recv() + et = time.monotonic() + r_echo = [] + while len(r_echo) < 1 and (time.monotonic() - st) < 10: + r_echo = p_send.can_recv() + + if len(r) == 0 or len(r_echo) == 0: + print(f"r: {r}, r_echo: {r_echo}") + + assert len(r) == 1 + assert 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 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 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)) + + # Run tests in both directions + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + test(p, panda_jungle) + test(panda_jungle, p) + + +@pytest.mark.panda_expect_can_error +@pytest.mark.test_panda_types(PandaGroup.GEN2) +def test_gen2_loopback(p, panda_jungle): + def test(p_send, p_recv, address=None): + 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 = address if address else 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, "address:", addr, "OBD:", obd, "OK") + + # Run tests in both directions + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + test(p, panda_jungle) + test(panda_jungle, p) + + # Test extended frame address with ELM327 mode + p.set_safety_mode(Panda.SAFETY_ELM327) + test(p, panda_jungle, 0x18DB33F1) + test(panda_jungle, p, 0x18DB33F1) + + # TODO: why it's not being reset by fixtures reinit? + p.set_obd(False) + panda_jungle.set_obd(False) + +def test_bulk_write(p, panda_jungle): + # The TX buffers on pandas is 0x100 in length. + NUM_MESSAGES_PER_BUS = 10000 + + def flood_tx(panda): + print('Sending!') + msg = b"\xaa" * 8 + packet = [] + # start with many messages on a single bus (higher contention for single TX ring buffer) + packet += [[0xaa, None, msg, 0]] * NUM_MESSAGES_PER_BUS + # end with many messages on multiple buses + packet += [[0xaa, None, msg, 0], [0xaa, None, msg, 1], [0xaa, None, msg, 2]] * NUM_MESSAGES_PER_BUS + + # Disable timeout + panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + panda.can_send_many(packet, timeout=0) + print(f"Done sending {4 * NUM_MESSAGES_PER_BUS} messages!", time.monotonic()) + print(panda.health()) + + # Start transmisson + threading.Thread(target=flood_tx, args=(p,)).start() + + # Receive as much as we can in a few second time period + rx = [] + old_len = 0 + start_time = time.monotonic() + while time.monotonic() - start_time < 5 or len(rx) > old_len: + old_len = len(rx) + rx.extend(panda_jungle.can_recv()) + print(f"Received {len(rx)} messages", time.monotonic()) + + # All messages should have been received + if len(rx) != 4 * NUM_MESSAGES_PER_BUS: + raise Exception("Did not receive all messages!") + +def test_message_integrity(p): + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + p.set_can_loopback(True) + for i in range(250): + sent_msgs = defaultdict(set) + for _ in range(random.randrange(10)): + to_send = get_random_can_messages(random.randrange(100)) + for m in to_send: + sent_msgs[m[3]].add((m[0], m[2])) + p.can_send_many(to_send, timeout=0) + + start_time = time.monotonic() + while time.monotonic() - start_time < 2 and any(len(sent_msgs[bus]) for bus in range(3)): + recvd = p.can_recv() + for msg in recvd: + if msg[3] >= 128: + k = (msg[0], bytes(msg[2])) + bus = msg[3]-128 + assert k in sent_msgs[bus], f"message {k} was never sent on bus {bus}" + sent_msgs[msg[3]-128].discard(k) + + # if a set isn't empty, messages got dropped + for bus in range(3): + assert not len(sent_msgs[bus]), f"loop {i}: bus {bus} missing {len(sent_msgs[bus])} messages" + + print("Got all messages intact") diff --git a/panda/tests/hitl/5_spi.py b/panda/tests/hitl/5_spi.py new file mode 100644 index 0000000..f9a4e28 --- /dev/null +++ b/panda/tests/hitl/5_spi.py @@ -0,0 +1,103 @@ +import binascii +import pytest +import random +from unittest.mock import patch + +from panda import Panda, PandaDFU +from panda.python.spi import SpiDevice, PandaProtocolMismatch, PandaSpiNackResponse + +pytestmark = [ + pytest.mark.test_panda_types((Panda.HW_TYPE_TRES, )) +] + +@pytest.mark.skip("doesn't work, bootloader seems to ignore commands once it sees junk") +def test_dfu_with_spam(p): + dfu_serial = p.get_dfu_serial() + + # enter DFU + p.reset(enter_bootstub=True) + p.reset(enter_bootloader=True) + assert Panda.wait_for_dfu(dfu_serial, timeout=19), "failed to enter DFU" + + # send junk + d = SpiDevice() + for _ in range(9): + with d.acquire() as spi: + dat = [random.randint(-1, 255) for _ in range(random.randint(1, 100))] + spi.xfer(dat) + + # should still show up + assert dfu_serial in PandaDFU.list() + +class TestSpi: + def _ping(self, mocker, panda): + # should work with no retries + spy = mocker.spy(panda._handle, '_wait_for_ack') + panda.health() + assert spy.call_count == 2 + mocker.stop(spy) + + def test_protocol_version_check(self, p): + for bootstub in (False, True): + p.reset(enter_bootstub=bootstub) + with patch('panda.python.spi.PandaSpiHandle.PROTOCOL_VERSION', return_value="abc"): + # list should still work with wrong version + assert p._serial in Panda.list() + + # connect but raise protocol error + with pytest.raises(PandaProtocolMismatch): + Panda(p._serial) + + def test_protocol_version_data(self, p): + for bootstub in (False, True): + p.reset(enter_bootstub=bootstub) + v = p._handle.get_protocol_version() + + uid = binascii.hexlify(v[:12]).decode() + assert uid == p.get_uid() + + hwtype = v[12] + assert hwtype == ord(p.get_type()) + + bstub = v[13] + assert bstub == (0xEE if bootstub else 0xCC) + + def test_all_comm_types(self, mocker, p): + spy = mocker.spy(p._handle, '_wait_for_ack') + + # controlRead + controlWrite + p.health() + p.can_clear(0) + assert spy.call_count == 2*2 + + # bulkRead + bulkWrite + p.can_recv() + p.can_send(0x123, b"somedata", 0) + assert spy.call_count == 2*4 + + def test_bad_header(self, mocker, p): + with patch('panda.python.spi.SYNC', return_value=0): + with pytest.raises(PandaSpiNackResponse): + p._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, p.HEALTH_STRUCT.size, timeout=50) + self._ping(mocker, p) + + def test_bad_checksum(self, mocker, p): + cnt = p.health()['spi_checksum_error_count'] + with patch('panda.python.spi.PandaSpiHandle._calc_checksum', return_value=0): + with pytest.raises(PandaSpiNackResponse): + p._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, p.HEALTH_STRUCT.size, timeout=50) + self._ping(mocker, p) + assert (p.health()['spi_checksum_error_count'] - cnt) > 0 + + def test_non_existent_endpoint(self, mocker, p): + for _ in range(10): + ep = random.randint(4, 20) + with pytest.raises(PandaSpiNackResponse): + p._handle.bulkRead(ep, random.randint(1, 1000), timeout=50) + + self._ping(mocker, p) + + with pytest.raises(PandaSpiNackResponse): + p._handle.bulkWrite(ep, b"abc", timeout=50) + + self._ping(mocker, p) diff --git a/panda/tests/hitl/6_safety.py b/panda/tests/hitl/6_safety.py new file mode 100644 index 0000000..2237f53 --- /dev/null +++ b/panda/tests/hitl/6_safety.py @@ -0,0 +1,29 @@ +import time + +from panda import Panda + + +def test_safety_nooutput(p): + p.set_safety_mode(Panda.SAFETY_SILENT) + p.set_can_loopback(True) + + # send a message on bus 0 + p.can_send(0x1aa, b"message", 0) + + # confirm receive nothing + time.sleep(0.05) + r = p.can_recv() + # bus 192 is messages blocked by TX safety hook on bus 0 + assert len([x for x in r if x[3] != 192]) == 0 + assert len([x for x in r if x[3] == 192]) == 1 + + +def test_canfd_safety_modes(p): + # works on all pandas + p.set_safety_mode(Panda.SAFETY_TOYOTA) + assert p.health()['safety_mode'] == Panda.SAFETY_TOYOTA + + # shouldn't be able to set a CAN-FD safety mode on non CAN-FD panda + p.set_safety_mode(Panda.SAFETY_HYUNDAI_CANFD) + expected_mode = Panda.SAFETY_HYUNDAI_CANFD if p.get_type() in Panda.H7_DEVICES else Panda.SAFETY_SILENT + assert p.health()['safety_mode'] == expected_mode diff --git a/panda/tests/hitl/7_internal.py b/panda/tests/hitl/7_internal.py new file mode 100644 index 0000000..eb8577f --- /dev/null +++ b/panda/tests/hitl/7_internal.py @@ -0,0 +1,68 @@ +import time +import pytest + +from panda import Panda + +pytestmark = [ + pytest.mark.skip_panda_types(Panda.HW_TYPE_UNO), + pytest.mark.test_panda_types(Panda.INTERNAL_DEVICES) +] + +@pytest.mark.timeout(2*60) +def test_fan_controller(p): + start_health = p.health() + + for power in (30, 50, 80, 100): + p.set_fan_power(0) + while p.get_fan_rpm() > 0: + time.sleep(0.1) + + # wait until fan spins up (and recovers if needed), + # then wait a bit more for the RPM to converge + p.set_fan_power(power) + for _ in range(20): + time.sleep(1) + if p.get_fan_rpm() > 1000: + break + time.sleep(5) + + expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * power / 100 + assert 0.9 * expected_rpm <= p.get_fan_rpm() <= 1.1 * expected_rpm + + # Ensure the stall detection is tested on dos + if p.get_type() == Panda.HW_TYPE_DOS: + stalls = p.health()['fan_stall_count'] - start_health['fan_stall_count'] + assert stalls >= 2 + print("stall count", stalls) + else: + assert p.health()['fan_stall_count'] == 0 + +def test_fan_cooldown(p): + # if the fan cooldown doesn't work, we get high frequency noise on the tach line + # while the rotor spins down. this makes sure it never goes beyond the expected max RPM + p.set_fan_power(100) + time.sleep(3) + p.set_fan_power(0) + for _ in range(5): + assert p.get_fan_rpm() <= 7000 + time.sleep(0.5) + +def test_fan_overshoot(p): + if p.get_type() == Panda.HW_TYPE_DOS: + pytest.skip("panda's fan controller overshoots on the comma three fans that need stall recovery") + + # make sure it's stopped completely + p.set_fan_power(0) + while p.get_fan_rpm() > 0: + time.sleep(0.1) + + # set it to 30% power to mimic going onroad + p.set_fan_power(30) + max_rpm = 0 + for _ in range(50): + max_rpm = max(max_rpm, p.get_fan_rpm()) + time.sleep(0.1) + + # tolerate 10% overshoot + expected_rpm = Panda.MAX_FAN_RPMs[bytes(p.get_type())] * 30 / 100 + assert max_rpm <= 1.1 * expected_rpm, f"Fan overshoot: {(max_rpm / expected_rpm * 100) - 100:.1f}%" diff --git a/panda/tests/hitl/__init__.py b/panda/tests/hitl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panda/tests/hitl/conftest.py b/panda/tests/hitl/conftest.py new file mode 100644 index 0000000..b784435 --- /dev/null +++ b/panda/tests/hitl/conftest.py @@ -0,0 +1,225 @@ +import os +import pytest +import concurrent.futures + +from panda import Panda, PandaDFU, PandaJungle +from panda.tests.hitl.helpers import clear_can_buffers + +# needed to get output when using xdist +if "DEBUG" in os.environ: + import sys + sys.stdout = sys.stderr + +SPEED_NORMAL = 500 +SPEED_GMLAN = 33.3 +BUS_SPEEDS = [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL), (3, SPEED_GMLAN)] + + +JUNGLE_SERIAL = os.getenv("PANDAS_JUNGLE") +NO_JUNGLE = os.environ.get("NO_JUNGLE", "0") == "1" +PANDAS_EXCLUDE = os.getenv("PANDAS_EXCLUDE", "").strip().split(" ") +HW_TYPES = os.environ.get("HW_TYPES", None) + +PARALLEL = "PARALLEL" in os.environ +NON_PARALLEL = "NON_PARALLEL" in os.environ +if PARALLEL: + NO_JUNGLE = True + +class PandaGroup: + H7 = (Panda.HW_TYPE_RED_PANDA, Panda.HW_TYPE_RED_PANDA_V2, Panda.HW_TYPE_TRES) + GEN2 = (Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO, Panda.HW_TYPE_DOS) + H7 + GMLAN = (Panda.HW_TYPE_WHITE_PANDA, Panda.HW_TYPE_GREY_PANDA) + + TESTED = (Panda.HW_TYPE_WHITE_PANDA, Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_RED_PANDA, Panda.HW_TYPE_RED_PANDA_V2, Panda.HW_TYPE_UNO) + +if HW_TYPES is not None: + PandaGroup.TESTED = [bytes([int(x), ]) for x in HW_TYPES.strip().split(",")] # type: ignore + + +# Find all pandas connected +_all_pandas = {} +_panda_jungle = None +def init_all_pandas(): + if not NO_JUNGLE: + global _panda_jungle + _panda_jungle = PandaJungle(JUNGLE_SERIAL) + _panda_jungle.set_panda_power(True) + + for serial in Panda.list(): + if serial not in PANDAS_EXCLUDE: + with Panda(serial=serial, claim=False) as p: + ptype = bytes(p.get_type()) + if ptype in PandaGroup.TESTED: + _all_pandas[serial] = ptype + + # ensure we have all tested panda types + missing_types = set(PandaGroup.TESTED) - set(_all_pandas.values()) + assert len(missing_types) == 0, f"Missing panda types: {missing_types}" + + print(f"{len(_all_pandas)} total pandas") +init_all_pandas() +_all_panda_serials = sorted(_all_pandas.keys()) + + +def init_jungle(): + if _panda_jungle is None: + return + clear_can_buffers(_panda_jungle) + _panda_jungle.set_panda_power(True) + _panda_jungle.set_can_loopback(False) + _panda_jungle.set_obd(False) + _panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1) + for bus, speed in BUS_SPEEDS: + _panda_jungle.set_can_speed_kbps(bus, speed) + + # ensure FW hasn't changed + assert _panda_jungle.up_to_date() + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "test_panda_types(name): whitelist a test for specific panda types" + ) + config.addinivalue_line( + "markers", "skip_panda_types(name): blacklist panda types from a test" + ) + config.addinivalue_line( + "markers", "panda_expect_can_error: mark test to ignore CAN health errors" + ) + +@pytest.hookimpl(tryfirst=True) +def pytest_collection_modifyitems(items): + for item in items: + if item.get_closest_marker('timeout') is None: + item.add_marker(pytest.mark.timeout(60)) + + # xdist grouping by panda + serial = item.name.split("serial=")[1].split(",")[0] + assert len(serial) == 24 + item.add_marker(pytest.mark.xdist_group(serial)) + + needs_jungle = "panda_jungle" in item.fixturenames + if PARALLEL and needs_jungle: + item.add_marker(pytest.mark.skip(reason="no jungle tests in PARALLEL mode")) + elif NON_PARALLEL and not needs_jungle: + item.add_marker(pytest.mark.skip(reason="only running jungle tests")) + +def pytest_make_parametrize_id(config, val, argname): + if val in _all_pandas: + # TODO: get nice string instead of int + hw_type = _all_pandas[val][0] + return f"serial={val}, hw_type={hw_type}" + return None + + +@pytest.fixture(name='panda_jungle', scope='function') +def fixture_panda_jungle(request): + init_jungle() + return _panda_jungle + +@pytest.fixture(name='p', scope='function') +def func_fixture_panda(request, module_panda): + p = module_panda + + # Check if test is applicable to this panda + mark = request.node.get_closest_marker('test_panda_types') + if mark: + assert len(mark.args) > 0, "Missing panda types argument in mark" + test_types = mark.args[0] + if _all_pandas[p.get_usb_serial()] not in test_types: + pytest.skip(f"Not applicable, {test_types} pandas only") + + mark = request.node.get_closest_marker('skip_panda_types') + if mark: + assert len(mark.args) > 0, "Missing panda types argument in mark" + skip_types = mark.args[0] + if _all_pandas[p.get_usb_serial()] in skip_types: + pytest.skip(f"Not applicable to {skip_types}") + + # TODO: reset is slow (2+ seconds) + p.reset() + + # ensure FW hasn't changed + assert p.up_to_date() + + # Run test + yield p + + # Teardown + + # reconnect + if p.get_dfu_serial() in PandaDFU.list(): + PandaDFU(p.get_dfu_serial()).reset() + p.reconnect() + if not p.connected: + p.reconnect() + if p.bootstub: + p.reset() + + assert not p.bootstub + + # TODO: would be nice to make these common checks in the teardown + # show up as failed tests instead of "errors" + + # Check for faults + assert p.health()['faults'] == 0 + assert p.health()['fault_status'] == 0 + + # Check for SPI errors + #assert p.health()['spi_checksum_error_count'] == 0 + + # Check health of each CAN core after test, normal to fail for test_gen2_loopback on OBD bus, so skipping + mark = request.node.get_closest_marker('panda_expect_can_error') + expect_can_error = mark is not None + if not expect_can_error: + for i in range(3): + can_health = p.can_health(i) + assert can_health['bus_off_cnt'] == 0 + assert can_health['receive_error_cnt'] < 127 + assert can_health['transmit_error_cnt'] < 255 + assert can_health['error_passive'] == 0 + assert can_health['error_warning'] == 0 + assert can_health['total_rx_lost_cnt'] == 0 + assert can_health['total_tx_lost_cnt'] == 0 + assert can_health['total_error_cnt'] == 0 + assert can_health['total_tx_checksum_error_cnt'] == 0 + +@pytest.fixture(name='module_panda', params=_all_panda_serials, scope='module') +def fixture_panda_setup(request): + """ + Clean up all pandas + jungle and return the panda under test. + """ + panda_serial = request.param + + # Initialize jungle + init_jungle() + + # Connect to pandas + def cnnct(s): + if s == panda_serial: + p = Panda(serial=s) + p.reset(reconnect=True) + + p.set_can_loopback(False) + p.set_gmlan(None) + p.set_power_save(False) + for bus, speed in BUS_SPEEDS: + p.set_can_speed_kbps(bus, speed) + clear_can_buffers(p) + p.set_power_save(False) + return p + elif not PARALLEL: + with Panda(serial=s) as p: + p.reset(reconnect=False) + return None + + with concurrent.futures.ThreadPoolExecutor() as exc: + ps = list(exc.map(cnnct, _all_panda_serials, timeout=20)) + pandas = [p for p in ps if p is not None] + + # run test + yield pandas[0] + + # Teardown + for p in pandas: + p.close() diff --git a/panda/tests/hitl/helpers.py b/panda/tests/hitl/helpers.py new file mode 100644 index 0000000..4eee437 --- /dev/null +++ b/panda/tests/hitl/helpers.py @@ -0,0 +1,71 @@ +import time +import random + + +def get_random_can_messages(n): + m = [] + for _ in range(n): + bus = random.randrange(3) + addr = random.randrange(1 << 29) + dat = bytes([random.getrandbits(8) for _ in range(random.randrange(1, 9))]) + m.append([addr, None, dat, bus]) + return m + + +def time_many_sends(p, bus, p_recv=None, msg_count=100, two_pandas=False, msg_len=8): + if p_recv is None: + p_recv = p + if p == p_recv and two_pandas: + raise ValueError("Cannot have two pandas that are the same panda") + + msg_id = random.randint(0x100, 0x200) + to_send = [(msg_id, 0, b"\xaa" * msg_len, bus)] * msg_count + + start_time = time.monotonic() + p.can_send_many(to_send) + r = [] + r_echo = [] + r_len_expected = msg_count if two_pandas else msg_count * 2 + r_echo_len_exected = msg_count if two_pandas else 0 + + while len(r) < r_len_expected and (time.monotonic() - start_time) < 5: + r.extend(p_recv.can_recv()) + end_time = time.monotonic() + if two_pandas: + while len(r_echo) < r_echo_len_exected and (time.monotonic() - start_time) < 10: + r_echo.extend(p.can_recv()) + + sent_echo = [x for x in r if x[3] == 0x80 | bus and x[0] == msg_id] + sent_echo.extend([x for x in r_echo if x[3] == 0x80 | bus and x[0] == msg_id]) + resp = [x for x in r if x[3] == bus and x[0] == msg_id] + + leftovers = [x for x in r if (x[3] != 0x80 | bus and x[3] != bus) or x[0] != msg_id] + assert len(leftovers) == 0 + + assert len(resp) == msg_count + assert len(sent_echo) == msg_count + + end_time = (end_time - start_time) * 1000.0 + comp_kbps = (1 + 11 + 1 + 1 + 1 + 4 + (msg_len * 8) + 15 + 1 + 1 + 1 + 7) * msg_count / end_time + + return comp_kbps + + +def clear_can_buffers(panda, speed: int | None = None): + if speed is not None: + for bus in range(3): + panda.set_can_speed_kbps(bus, speed) + + # clear tx buffers + for i in range(4): + panda.can_clear(i) + + # clear rx buffers + panda.can_clear(0xFFFF) + r = [1] + st = time.monotonic() + while len(r) > 0: + r = panda.can_recv() + time.sleep(0.05) + if (time.monotonic() - st) > 10: + raise Exception("Unable to clear can buffers for panda ", panda.get_serial()) diff --git a/panda/tests/hitl/known_bootstub/bootstub.panda_h7.bin b/panda/tests/hitl/known_bootstub/bootstub.panda_h7.bin new file mode 100644 index 0000000000000000000000000000000000000000..a6d856bd2c7006ca022f8bb565fcd841451fe442 GIT binary patch literal 16824 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%0Dml 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 literal 0 HcmV?d00001 diff --git a/panda/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin b/panda/tests/hitl/known_bootstub/bootstub_f4_only_bcd.panda.bin new file mode 100644 index 0000000000000000000000000000000000000000..000fd26fe47b95ff212f30ecaa02f6b99e413b53 GIT binary patch literal 14208 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 literal 0 HcmV?d00001 diff --git a/panda/tests/hitl/reset_jungles.py b/panda/tests/hitl/reset_jungles.py new file mode 100644 index 0000000..fb94673 --- /dev/null +++ b/panda/tests/hitl/reset_jungles.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +import concurrent.futures + +from panda import PandaJungle, PandaJungleDFU, McuType +from panda.tests.libs.resetter import Resetter + +SERIALS = {'180019001451313236343430', '1d0017000c50435635333720'} + +def recover(s): + with PandaJungleDFU(s) as pd: + pd.recover() + +def flash(s): + with PandaJungle(s) as p: + p.flash() + return p.get_mcu_type() + +# Reset + flash all CI hardware to get it into a consistent state +# * port 1: jungles-under-test +# * port 2: USB hubs +# * port 3: HITL pandas and their jungles +if __name__ == "__main__": + with Resetter() as r: + # everything off + for i in range(1, 4): + r.enable_power(i, 0) + r.cycle_power(ports=[1, 2], dfu=True) + + dfu_serials = PandaJungleDFU.list() + print(len(dfu_serials), len(SERIALS)) + assert len(dfu_serials) == len(SERIALS) + + with concurrent.futures.ProcessPoolExecutor(max_workers=len(dfu_serials)) as exc: + list(exc.map(recover, dfu_serials, timeout=30)) + + # power cycle for H7 bootloader bug + r.cycle_power(ports=[1, 2]) + + serials = PandaJungle.list() + assert set(PandaJungle.list()) >= SERIALS + mcu_types = list(exc.map(flash, SERIALS, timeout=20)) + assert set(mcu_types) == {McuType.F4, McuType.H7} diff --git a/panda/tests/hitl/run_parallel_tests.sh b/panda/tests/hitl/run_parallel_tests.sh new file mode 100644 index 0000000..b6b79d9 --- /dev/null +++ b/panda/tests/hitl/run_parallel_tests.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +# n = number of pandas tested +PARALLEL=1 pytest --durations=0 *.py -n 5 --dist loadgroup -x diff --git a/panda/tests/hitl/run_serial_tests.sh b/panda/tests/hitl/run_serial_tests.sh new file mode 100644 index 0000000..31270f0 --- /dev/null +++ b/panda/tests/hitl/run_serial_tests.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +NON_PARALLEL=1 pytest --durations=0 *.py -x diff --git a/panda/tests/ir_test.py b/panda/tests/ir_test.py new file mode 100644 index 0000000..e41decf --- /dev/null +++ b/panda/tests/ir_test.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +import time + +from panda import Panda + +power = 0 +if __name__ == "__main__": + p = Panda() + while True: + p.set_ir_power(power) + print("Power: ", str(power)) + time.sleep(1) + power += 10 + power %= 100 diff --git a/panda/tests/libpanda/SConscript b/panda/tests/libpanda/SConscript new file mode 100644 index 0000000..cc08907 --- /dev/null +++ b/panda/tests/libpanda/SConscript @@ -0,0 +1,42 @@ +import platform + +CC = 'gcc' +system = platform.system() +if system == 'Darwin': + # gcc installed by homebrew has version suffix (e.g. gcc-12) in order to be + # distinguishable from system one - which acts as a symlink to clang + CC += '-13' + +env = Environment( + CC=CC, + CFLAGS=[ + '-nostdlib', + '-fno-builtin', + '-std=gnu11', + '-Wfatal-errors', + '-Wno-pointer-to-int-cast', + ], + CPPPATH=[".", "../../board/"], +) +if system == "Darwin": + env.PrependENVPath('PATH', '/opt/homebrew/bin') + +if GetOption('ubsan'): + flags = [ + "-fsanitize=undefined", + "-fno-sanitize-recover=undefined", + ] + env['CFLAGS'] += flags + env['LINKFLAGS'] += flags + +panda = env.SharedObject("panda.os", "panda.c") +libpanda = env.SharedLibrary("libpanda.so", [panda]) + +if GetOption('coverage'): + env.Append( + CFLAGS=["-fprofile-arcs", "-ftest-coverage", "-fprofile-abs-path",], + LIBS=["gcov"], + ) + # GCC note file is generated by compiler, ensure we build it, and allow scons to clean it up + AlwaysBuild(panda) + env.SideEffect("panda.gcno", panda) diff --git a/panda/tests/libpanda/libpanda_py.py b/panda/tests/libpanda/libpanda_py.py new file mode 100644 index 0000000..8f876c5 --- /dev/null +++ b/panda/tests/libpanda/libpanda_py.py @@ -0,0 +1,98 @@ +import os +from cffi import FFI +from typing import Any, Protocol + +from panda import LEN_TO_DLC +from panda.tests.libpanda.safety_helpers import PandaSafety, setup_safety_helpers + +libpanda_dir = os.path.dirname(os.path.abspath(__file__)) +libpanda_fn = os.path.join(libpanda_dir, "libpanda.so") + +ffi = FFI() + +ffi.cdef(""" +typedef struct { + unsigned char reserved : 1; + unsigned char bus : 3; + unsigned char data_len_code : 4; + unsigned char rejected : 1; + unsigned char returned : 1; + unsigned char extended : 1; + unsigned int addr : 29; + unsigned char checksum; + unsigned char data[64]; +} CANPacket_t; +""", packed=True) + +ffi.cdef(""" +bool safety_rx_hook(CANPacket_t *to_send); +bool safety_tx_hook(CANPacket_t *to_push); +int safety_fwd_hook(int bus_num, int addr); +int set_safety_hooks(uint16_t mode, uint16_t param); +""") + +ffi.cdef(""" +typedef struct { + volatile uint32_t w_ptr; + volatile uint32_t r_ptr; + uint32_t fifo_size; + CANPacket_t *elems; +} can_ring; + +extern can_ring *rx_q; +extern can_ring *txgmlan_q; +extern can_ring *tx1_q; +extern can_ring *tx2_q; +extern can_ring *tx3_q; + +bool can_pop(can_ring *q, CANPacket_t *elem); +bool can_push(can_ring *q, CANPacket_t *elem); +void can_set_checksum(CANPacket_t *packet); +int comms_can_read(uint8_t *data, uint32_t max_len); +void comms_can_write(uint8_t *data, uint32_t len); +void comms_can_reset(void); +uint32_t can_slots_empty(can_ring *q); +""") + +setup_safety_helpers(ffi) + +class CANPacket: + reserved: int + bus: int + data_len_code: int + rejected: int + returned: int + extended: int + addr: int + data: list[int] + +class Panda(PandaSafety, Protocol): + # CAN + tx1_q: Any + tx2_q: Any + tx3_q: Any + txgmlan_q: Any + def can_set_checksum(self, p: CANPacket) -> None: ... + + # safety + def safety_rx_hook(self, to_send: CANPacket) -> int: ... + def safety_tx_hook(self, to_push: CANPacket) -> int: ... + def safety_fwd_hook(self, bus_num: int, addr: int) -> int: ... + def set_safety_hooks(self, mode: int, param: int) -> int: ... + + +libpanda: Panda = ffi.dlopen(libpanda_fn) + + +# helpers + +def make_CANPacket(addr: int, bus: int, dat): + ret = ffi.new('CANPacket_t *') + ret[0].extended = 1 if addr >= 0x800 else 0 + ret[0].addr = addr + ret[0].data_len_code = LEN_TO_DLC[len(dat)] + ret[0].bus = bus + ret[0].data = bytes(dat) + libpanda.can_set_checksum(ret) + + return ret diff --git a/panda/tests/libpanda/panda.c b/panda/tests/libpanda/panda.c new file mode 100644 index 0000000..8efb6de --- /dev/null +++ b/panda/tests/libpanda/panda.c @@ -0,0 +1,33 @@ +#include "fake_stm.h" +#include "config.h" +#include "can_definitions.h" + +bool bitbang_gmlan(CANPacket_t *to_bang) { return true; } +bool can_init(uint8_t can_number) { return true; } +void process_can(uint8_t can_number) { } +//int safety_tx_hook(CANPacket_t *to_send) { return 1; } + +typedef struct harness_configuration harness_configuration; +void refresh_can_tx_slots_available(void); +void can_tx_comms_resume_usb(void) { }; +void can_tx_comms_resume_spi(void) { }; + +#include "health.h" +#include "faults.h" +#include "libc.h" +#include "boards/board_declarations.h" +#include "safety.h" +#include "main_declarations.h" +#include "drivers/can_common.h" + +can_ring *rx_q = &can_rx_q; +can_ring *txgmlan_q = &can_txgmlan_q; +can_ring *tx1_q = &can_tx1_q; +can_ring *tx2_q = &can_tx2_q; +can_ring *tx3_q = &can_tx3_q; + +#include "comms_definitions.h" +#include "can_comms.h" + +// libpanda stuff +#include "safety_helpers.h" diff --git a/panda/tests/libpanda/safety_helpers.h b/panda/tests/libpanda/safety_helpers.h new file mode 100644 index 0000000..074463d --- /dev/null +++ b/panda/tests/libpanda/safety_helpers.h @@ -0,0 +1,195 @@ +void safety_tick_current_safety_config() { + safety_tick(¤t_safety_config); +} + +bool safety_config_valid() { + if (current_safety_config.rx_checks_len <= 0) { + printf("missing RX checks\n"); + return false; + } + + for (int i = 0; i < current_safety_config.rx_checks_len; i++) { + const RxCheck addr = current_safety_config.rx_checks[i]; + bool valid = addr.status.msg_seen && !addr.status.lagging && addr.status.valid_checksum && (addr.status.wrong_counters < MAX_WRONG_COUNTERS) && addr.status.valid_quality_flag; + if (!valid) { + // printf("i %d seen %d lagging %d valid checksum %d wrong counters %d valid quality flag %d\n", i, addr.status.msg_seen, addr.status.lagging, addr.status.valid_checksum, addr.status.wrong_counters, addr.status.valid_quality_flag); + return false; + } + } + return true; +} + +void set_controls_allowed(bool c){ + controls_allowed = c; +} + +void set_alternative_experience(int mode){ + alternative_experience = mode; +} + +void set_relay_malfunction(bool c){ + relay_malfunction = c; +} + +bool get_controls_allowed(void){ + return controls_allowed; +} + +int get_alternative_experience(void){ + return alternative_experience; +} + +bool get_relay_malfunction(void){ + return relay_malfunction; +} + +int get_gas_interceptor_prev(void){ + return gas_interceptor_prev; +} + +bool get_gas_pressed_prev(void){ + return gas_pressed_prev; +} + +bool get_brake_pressed_prev(void){ + return brake_pressed_prev; +} + +bool get_regen_braking_prev(void){ + return regen_braking_prev; +} + +bool get_cruise_engaged_prev(void){ + return cruise_engaged_prev; +} + +void set_cruise_engaged_prev(bool engaged){ + cruise_engaged_prev = engaged; +} + +bool get_vehicle_moving(void){ + return vehicle_moving; +} + +bool get_acc_main_on(void){ + return acc_main_on; +} + +int get_vehicle_speed_min(void){ + return vehicle_speed.min; +} + +int get_vehicle_speed_max(void){ + return vehicle_speed.max; +} + +int get_vehicle_speed_last(void){ + return vehicle_speed.values[0]; +} + +int get_current_safety_mode(void){ + return current_safety_mode; +} + +int get_current_safety_param(void){ + return current_safety_param; +} + +int get_hw_type(void){ + return hw_type; +} + +void set_timer(uint32_t t){ + timer.CNT = t; +} + +void set_torque_meas(int min, int max){ + torque_meas.min = min; + torque_meas.max = max; +} + +int get_torque_meas_min(void){ + return torque_meas.min; +} + +int get_torque_meas_max(void){ + return torque_meas.max; +} + +void set_torque_driver(int min, int max){ + torque_driver.min = min; + torque_driver.max = max; +} + +int get_torque_driver_min(void){ + return torque_driver.min; +} + +int get_torque_driver_max(void){ + return torque_driver.max; +} + +void set_rt_torque_last(int t){ + rt_torque_last = t; +} + +void set_desired_torque_last(int t){ + desired_torque_last = t; +} + +void set_desired_angle_last(int t){ + desired_angle_last = t; +} + +int get_desired_angle_last(void){ + return desired_angle_last; +} + +void set_angle_meas(int min, int max){ + angle_meas.min = min; + angle_meas.max = max; +} + +int get_angle_meas_min(void){ + return angle_meas.min; +} + +int get_angle_meas_max(void){ + return angle_meas.max; +} + + +// ***** car specific helpers ***** + +void set_honda_alt_brake_msg(bool c){ + honda_alt_brake_msg = c; +} + +void set_honda_bosch_long(bool c){ + honda_bosch_long = c; +} + +int get_honda_hw(void) { + return honda_hw; +} + +void set_honda_fwd_brake(bool c){ + honda_fwd_brake = c; +} + +bool get_honda_fwd_brake(void){ + return honda_fwd_brake; +} + +void init_tests(void){ + // get HW_TYPE from env variable set in test.sh + if (getenv("HW_TYPE")) { + hw_type = atoi(getenv("HW_TYPE")); + } + safety_mode_cnt = 2U; // avoid ignoring relay_malfunction logic + alternative_experience = 0; + set_timer(0); + ts_steer_req_mismatch_last = 0; + valid_steer_req_count = 0; + invalid_steer_req_count = 0; +} diff --git a/panda/tests/libpanda/safety_helpers.py b/panda/tests/libpanda/safety_helpers.py new file mode 100644 index 0000000..28f3349 --- /dev/null +++ b/panda/tests/libpanda/safety_helpers.py @@ -0,0 +1,106 @@ +# panda safety helpers, from safety_helpers.c +from typing import Protocol + +def setup_safety_helpers(ffi): + ffi.cdef(""" + void set_controls_allowed(bool c); + bool get_controls_allowed(void); + bool get_longitudinal_allowed(void); + void set_alternative_experience(int mode); + int get_alternative_experience(void); + void set_relay_malfunction(bool c); + bool get_relay_malfunction(void); + int get_gas_interceptor_prev(void); + bool get_gas_pressed_prev(void); + bool get_brake_pressed_prev(void); + bool get_regen_braking_prev(void); + bool get_acc_main_on(void); + int get_vehicle_speed_min(void); + int get_vehicle_speed_max(void); + int get_vehicle_speed_last(void); + int get_current_safety_mode(void); + int get_current_safety_param(void); + + void set_torque_meas(int min, int max); + int get_torque_meas_min(void); + int get_torque_meas_max(void); + void set_torque_driver(int min, int max); + int get_torque_driver_min(void); + int get_torque_driver_max(void); + void set_desired_torque_last(int t); + void set_rt_torque_last(int t); + void set_desired_angle_last(int t); + int get_desired_angle_last(); + void set_angle_meas(int min, int max); + int get_angle_meas_min(void); + int get_angle_meas_max(void); + + bool get_cruise_engaged_prev(void); + void set_cruise_engaged_prev(bool engaged); + bool get_vehicle_moving(void); + int get_hw_type(void); + void set_timer(uint32_t t); + + void safety_tick_current_safety_config(); + bool safety_config_valid(); + + void init_tests(void); + + void set_honda_fwd_brake(bool c); + bool get_honda_fwd_brake(void); + void set_honda_alt_brake_msg(bool c); + void set_honda_bosch_long(bool c); + int get_honda_hw(void); + """) + +class PandaSafety(Protocol): + def set_controls_allowed(self, c: bool) -> None: ... + def get_controls_allowed(self) -> bool: ... + def get_longitudinal_allowed(self) -> bool: ... + def set_alternative_experience(self, mode: int) -> None: ... + def get_alternative_experience(self) -> int: ... + def set_relay_malfunction(self, c: bool) -> None: ... + def get_relay_malfunction(self) -> bool: ... + def get_gas_interceptor_prev(self) -> int: ... + def get_gas_pressed_prev(self) -> bool: ... + def get_brake_pressed_prev(self) -> bool: ... + def get_regen_braking_prev(self) -> bool: ... + def get_acc_main_on(self) -> bool: ... + def get_vehicle_speed_min(self) -> int: ... + def get_vehicle_speed_max(self) -> int: ... + def get_vehicle_speed_last(self) -> int: ... + def get_current_safety_mode(self) -> int: ... + def get_current_safety_param(self) -> int: ... + + def set_torque_meas(self, min: int, max: int) -> None: ... # noqa: A002 + def get_torque_meas_min(self) -> int: ... + def get_torque_meas_max(self) -> int: ... + def set_torque_driver(self, min: int, max: int) -> None: ... # noqa: A002 + def get_torque_driver_min(self) -> int: ... + def get_torque_driver_max(self) -> int: ... + def set_desired_torque_last(self, t: int) -> None: ... + def set_rt_torque_last(self, t: int) -> None: ... + def set_desired_angle_last(self, t: int) -> None: ... + def get_desired_angle_last(self) -> int: ... + def set_angle_meas(self, min: int, max: int) -> None: ... # noqa: A002 + def get_angle_meas_min(self) -> int: ... + def get_angle_meas_max(self) -> int: ... + + def get_cruise_engaged_prev(self) -> bool: ... + def set_cruise_engaged_prev(self, enabled: bool) -> None: ... + def get_vehicle_moving(self) -> bool: ... + def get_hw_type(self) -> int: ... + def set_timer(self, t: int) -> None: ... + + def safety_tick_current_safety_config(self) -> None: ... + def safety_config_valid(self) -> bool: ... + + def init_tests(self) -> None: ... + + def set_honda_fwd_brake(self, c: bool) -> None: ... + def get_honda_fwd_brake(self) -> bool: ... + def set_honda_alt_brake_msg(self, c: bool) -> None: ... + def set_honda_bosch_long(self, c: bool) -> None: ... + def get_honda_hw(self) -> int: ... + + diff --git a/panda/tests/libs/resetter.py b/panda/tests/libs/resetter.py new file mode 100644 index 0000000..3868bde --- /dev/null +++ b/panda/tests/libs/resetter.py @@ -0,0 +1,57 @@ +import time +import usb1 + + +class Resetter(): + def __init__(self): + self._handle = None + self.connect() + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + self._handle.close() + self._context.close() + self._handle = None + + def connect(self): + if self._handle: + self.close() + + self._handle = None + + self._context = usb1.USBContext() + self._context.open() + for device in self._context.getDeviceList(skip_on_error=True): + if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddc0: + try: + self._handle = device.open() + self._handle.claimInterface(0) + break + except Exception as e: + print(e) + assert self._handle + + def enable_power(self, port, enabled): + self._handle.controlWrite((usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE), 0xff, port, enabled, b'') + + def enable_boot(self, enabled): + self._handle.controlWrite((usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE), 0xff, 0, enabled, b'') + + def cycle_power(self, delay=5, dfu=False, ports=None): + if ports is None: + ports = [1, 2, 3] + + self.enable_boot(dfu) + for port in ports: + self.enable_power(port, False) + time.sleep(0.5) + + for port in ports: + self.enable_power(port, True) + time.sleep(delay) + self.enable_boot(False) diff --git a/panda/tests/loopback_test.py b/panda/tests/loopback_test.py new file mode 100644 index 0000000..d4f8beb --- /dev/null +++ b/panda/tests/loopback_test.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +import os +import time +import random +import argparse +from itertools import permutations + +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +def run_test(sleep_duration): + pandas = Panda.list() + print(pandas) + + if len(pandas) < 2: + raise Exception("Minimum two pandas are needed for test") + + run_test_w_pandas(pandas, sleep_duration) + +def run_test_w_pandas(pandas, sleep_duration): + h = [Panda(x) for x in pandas] + print("H", h) + + for hh in h: + hh.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test both directions + for ho in permutations(list(range(len(h))), r=2): + print("***************** TESTING", ho) + + panda0, panda1 = h[ho[0]], h[ho[1]] + + # **** test health packet **** + print("health", ho[0], h[ho[0]].health()) + + # **** test can line loopback **** + for bus, gmlan in [(0, False), (1, False), (2, False), (1, True), (2, True)]: + print("\ntest can", bus) + # flush + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + panda0.set_gmlan(None) + panda1.set_gmlan(None) + + if gmlan is True: + panda0.set_gmlan(bus) + panda1.set_gmlan(bus) + bus = 3 + + # send the characters + at = random.randint(1, 2000) + st = get_test_string()[0:8] + panda0.can_send(at, st, bus) + time.sleep(0.1) + + # check for receive + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + print("Bus", bus, "echo", cans_echo, "loop", cans_loop) + + assert len(cans_echo) == 1 + assert len(cans_loop) == 1 + + assert cans_echo[0][0] == at + assert cans_loop[0][0] == at + + assert cans_echo[0][2] == st + assert cans_loop[0][2] == st + + assert cans_echo[0][3] == 0x80 | bus + if cans_loop[0][3] != bus: + print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3])) + assert cans_loop[0][3] == bus + + print("CAN pass", bus, ho) + time.sleep(sleep_duration) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for _ in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/panda/tests/message_drop_test.py b/panda/tests/message_drop_test.py new file mode 100644 index 0000000..bf485e4 --- /dev/null +++ b/panda/tests/message_drop_test.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import os +import usb1 +import time +import struct +import itertools +import threading +from typing import Any + +from panda import Panda + +JUNGLE = "JUNGLE" in os.environ +if JUNGLE: + from panda import PandaJungle + +# Generate unique messages +NUM_MESSAGES_PER_BUS = 10000 +messages = [bytes(struct.pack("Q", i)) for i in range(NUM_MESSAGES_PER_BUS)] +tx_messages = list(itertools.chain.from_iterable([[0xaa, None, msg, 0], [0xaa, None, msg, 1], [0xaa, None, msg, 2]] for msg in messages)) + +def flood_tx(panda): + print('Sending!') + transferred = 0 + while True: + try: + print(f"Sending block {transferred}-{len(tx_messages)}: ", end="") + panda.can_send_many(tx_messages[transferred:], timeout=10) + print("OK") + break + except usb1.USBErrorTimeout as e: + transferred += (e.transferred // 16) + print("timeout, transferred: ", transferred) + + print(f"Done sending {3*NUM_MESSAGES_PER_BUS} messages!") + +if __name__ == "__main__": + serials = Panda.list() + receiver: Panda | PandaJungle + if JUNGLE: + sender = Panda() + receiver = PandaJungle() + else: + if len(serials) != 2: + raise Exception("Connect two pandas to perform this test!") + sender = Panda(serials[0]) + receiver = Panda(serials[1]) + receiver.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # Start transmisson + threading.Thread(target=flood_tx, args=(sender,)).start() + + # Receive as much as we can, and stop when there hasn't been anything for a second + rx: list[Any] = [] + old_len = 0 + last_change = time.monotonic() + while time.monotonic() - last_change < 1: + if old_len < len(rx): + last_change = time.monotonic() + old_len = len(rx) + + rx.extend(receiver.can_recv()) + print(f"Received {len(rx)} messages") + + # Check if we received everything + for bus in range(3): + received_msgs = {bytes(m[2]) for m in filter(lambda m, b=bus: m[3] == b, rx)} # type: ignore + dropped_msgs = set(messages).difference(received_msgs) + print(f"Bus {bus} dropped msgs: {len(list(dropped_msgs))} / {len(messages)}") diff --git a/panda/tests/misra/.gitignore b/panda/tests/misra/.gitignore new file mode 100644 index 0000000..fc9ac22 --- /dev/null +++ b/panda/tests/misra/.gitignore @@ -0,0 +1,5 @@ +*.pdf +*.txt +.output.log +new_table +cppcheck/ diff --git a/panda/tests/misra/coverage_table b/panda/tests/misra/coverage_table new file mode 100644 index 0000000..0395aba --- /dev/null +++ b/panda/tests/misra/coverage_table @@ -0,0 +1,156 @@ +1.1 +1.2 X (Addon) +1.3 X (Cppcheck) +2.1 X (Cppcheck) +2.2 X (Addon) +2.3 X (Addon) +2.4 X (Addon) +2.5 X (Addon) +2.6 X (Cppcheck) +2.7 X (Addon) +3.1 X (Addon) +3.2 X (Addon) +4.1 X (Addon) +4.2 X (Addon) +5.1 X (Addon) +5.2 X (Addon) +5.3 X (Cppcheck) +5.4 X (Addon) +5.5 X (Addon) +5.6 X (Addon) +5.7 X (Addon) +5.8 X (Addon) +5.9 X (Addon) +6.1 X (Addon) +6.2 X (Addon) +7.1 X (Addon) +7.2 X (Addon) +7.3 X (Addon) +7.4 X (Addon) +8.1 X (Addon) +8.2 X (Addon) +8.3 X (Cppcheck) +8.4 X (Addon) +8.5 X (Addon) +8.6 X (Addon) +8.7 X (Addon) +8.8 X (Addon) +8.9 X (Addon) +8.10 X (Addon) +8.11 X (Addon) +8.12 X (Addon) +8.13 X (Cppcheck) +8.14 X (Addon) +9.1 X (Cppcheck) +9.2 X (Addon) +9.3 X (Addon) +9.4 X (Addon) +9.5 X (Addon) +10.1 X (Addon) +10.2 X (Addon) +10.3 X (Addon) +10.4 X (Addon) +10.5 X (Addon) +10.6 X (Addon) +10.7 X (Addon) +10.8 X (Addon) +11.1 X (Addon) +11.2 X (Addon) +11.3 X (Addon) +11.4 X (Addon) +11.5 X (Addon) +11.6 X (Addon) +11.7 X (Addon) +11.8 X (Addon) +11.9 X (Addon) +12.1 X (Addon) +12.2 X (Addon) +12.3 X (Addon) +12.4 X (Addon) +13.1 X (Addon) +13.2 X (Cppcheck) +13.3 X (Addon) +13.4 X (Addon) +13.5 X (Addon) +13.6 X (Addon) +14.1 X (Addon) +14.2 X (Addon) +14.3 X (Cppcheck) +14.4 X (Addon) +15.1 X (Addon) +15.2 X (Addon) +15.3 X (Addon) +15.4 X (Addon) +15.5 X (Addon) +15.6 X (Addon) +15.7 X (Addon) +16.1 X (Addon) +16.2 X (Addon) +16.3 X (Addon) +16.4 X (Addon) +16.5 X (Addon) +16.6 X (Addon) +16.7 X (Addon) +17.1 X (Addon) +17.2 X (Addon) +17.3 X (Addon) +17.4 X (Cppcheck) +17.5 X (Cppcheck) +17.6 X (Addon) +17.7 X (Addon) +17.8 X (Addon) +18.1 X (Cppcheck) +18.2 X (Cppcheck) +18.3 X (Cppcheck) +18.4 X (Addon) +18.5 X (Addon) +18.6 X (Cppcheck) +18.7 X (Addon) +18.8 X (Addon) +19.1 X (Cppcheck) +19.2 X (Addon) +20.1 X (Addon) +20.2 X (Addon) +20.3 X (Addon) +20.4 X (Addon) +20.5 X (Addon) +20.6 X (Cppcheck) +20.7 X (Addon) +20.8 X (Addon) +20.9 X (Addon) +20.10 X (Addon) +20.11 X (Addon) +20.12 X (Addon) +20.13 X (Addon) +20.14 X (Addon) +21.1 X (Addon) +21.2 X (Addon) +21.3 X (Addon) +21.4 X (Addon) +21.5 X (Addon) +21.6 X (Addon) +21.7 X (Addon) +21.8 X (Addon) +21.9 X (Addon) +21.10 X (Addon) +21.11 X (Addon) +21.12 X (Addon) +21.13 X (Cppcheck) +21.14 X (Addon) +21.15 X (Addon) +21.16 X (Addon) +21.17 X (Cppcheck) +21.18 X (Cppcheck) +21.19 X (Addon) +21.20 X (Addon) +21.21 X (Addon) +22.1 X (Cppcheck) +22.2 X (Cppcheck) +22.3 X (Cppcheck) +22.4 X (Cppcheck) +22.5 X (Addon) +22.6 X (Cppcheck) +22.7 X (Addon) +22.8 X (Addon) +22.9 X (Addon) +22.10 X (Addon) diff --git a/panda/tests/misra/install.sh b/panda/tests/misra/install.sh new file mode 100644 index 0000000..ecc0b3f --- /dev/null +++ b/panda/tests/misra/install.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +: "${CPPCHECK_DIR:=$DIR/cppcheck/}" + +if [ ! -d "$CPPCHECK_DIR" ]; then + git clone https://github.com/danmar/cppcheck.git $CPPCHECK_DIR +fi + +cd $CPPCHECK_DIR + +VERS="2.13.0" +git fetch --all --tags +git checkout $VERS +git cherry-pick -n f6b538e855f0bacea33c4074664628024ef39dc6 b11b42087ff29569bc3740f5aa07eb6616ea4f63 + +#make clean +make MATCHCOMPILTER=yes CXXFLAGS="-O2" -j8 diff --git a/panda/tests/misra/test_misra.sh b/panda/tests/misra/test_misra.sh new file mode 100644 index 0000000..9fdbea1 --- /dev/null +++ b/panda/tests/misra/test_misra.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PANDA_DIR=$(realpath $DIR/../../) + +GREEN="\e[1;32m" +NC='\033[0m' + +: "${CPPCHECK_DIR:=$DIR/cppcheck/}" + +# install cppcheck if missing +if [ -z "${SKIP_BUILD}" ]; then + $DIR/install.sh +fi + +# ensure checked in coverage table is up to date +cd $DIR +python $CPPCHECK_DIR/addons/misra.py -generate-table > new_table +if ! cmp -s new_table coverage_table; then + echo "MISRA coverage table doesn't match. Update and commit:" + echo "mv new_table coverage_table && git add . && git commit -m 'update table'" + exit 1 +fi + +cd $PANDA_DIR +if [ -z "${SKIP_BUILD}" ]; then + scons -j8 +fi + +cppcheck() { + # note that cppcheck build cache results in inconsistent results as of v2.13.0 + OUTPUT=$DIR/.output.log + $CPPCHECK_DIR/cppcheck --force --inline-suppr -I $PANDA_DIR/board/ \ + -I $gcc_inc "$(arm-none-eabi-gcc -print-file-name=include)" \ + --suppressions-list=$DIR/suppressions.txt --suppress=*:*inc/* \ + --suppress=*:*include/* --error-exitcode=2 --check-level=exhaustive \ + --platform=arm32-wchar_t2 \ + "$@" |& tee $OUTPUT + + # cppcheck bug: some MISRA errors won't result in the error exit code, + # so check the output (https://trac.cppcheck.net/ticket/12440#no1) + if grep -e "misra violation" -e "error" -e "style: " $OUTPUT > /dev/null; then + exit 1 + fi +} + +PANDA_OPTS="--enable=all --disable=unusedFunction -DPANDA --addon=misra" + +printf "\n${GREEN}** PANDA F4 CODE **${NC}\n" +cppcheck $PANDA_OPTS -DSTM32F4 -DUID_BASE $PANDA_DIR/board/main.c + +printf "\n${GREEN}** PANDA H7 CODE **${NC}\n" +cppcheck $PANDA_OPTS -DSTM32H7 -DUID_BASE $PANDA_DIR/board/main.c + +# unused needs to run globally +#printf "\n${GREEN}** UNUSED ALL CODE **${NC}\n" +#cppcheck --enable=unusedFunction --quiet $PANDA_DIR/board/ + +printf "\n${GREEN}Success!${NC} took $SECONDS seconds\n" + diff --git a/panda/tests/misra/test_mutation.py b/panda/tests/misra/test_mutation.py new file mode 100644 index 0000000..9e778e8 --- /dev/null +++ b/panda/tests/misra/test_mutation.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +import os +import glob +import pytest +import shutil +import subprocess +import tempfile +import random + +HERE = os.path.abspath(os.path.dirname(__file__)) +ROOT = os.path.join(HERE, "../../") + +IGNORED_PATHS = ( + 'board/obj', + 'board/jungle', + 'board/stm32h7/inc', + 'board/stm32f4/inc', + 'board/fake_stm.h', + + # bootstub only files + 'board/flasher.h', + 'board/bootstub.c', + 'board/bootstub_declarations.h', + 'board/stm32f4/llflash.h' +) + +mutations = [ + # default + (None, None, False), + # F4 only + ("board/stm32f4/llbxcan.h", "s/1U/1/g", True), + # H7 only + ("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True), + # general safety + ("board/safety/safety_toyota.h", "s/is_lkas_msg =.*;/is_lkas_msg = addr == 1 || addr == 2;/g", True), +] + +patterns = [ + # misra-c2012-13.3 + "$a void test(int tmp) { int tmp2 = tmp++ + 2; if (tmp2) {;}}", + # misra-c2012-13.4 + "$a int test(int x, int y) { return (x=2) && (y=2); }", + # misra-c2012-13.5 + "$a void test(int tmp) { if (true && tmp++) {;} }", + # misra-c2012-13.6 + "$a void test(int tmp) { if (sizeof(tmp++)) {;} }", + # misra-c2012-14.1 + "$a void test(float len) { for (float j = 0; j < len; j++) {;} }", + # misra-c2012-14.4 + "$a void test(int len) { if (len - 8) {;} }", + # misra-c2012-16.4 + r"$a void test(int temp) {switch (temp) { case 1: ; }}\n", + # misra-c2012-17.8 + "$a void test(int cnt) { for (cnt=0;;cnt++) {;} }", + # misra-c2012-20.4 + r"$a #define auto 1\n", + # misra-c2012-20.5 + r"$a #define TEST 1\n#undef TEST\n", +] + +all_files = glob.glob('board/**', root_dir=ROOT, recursive=True) +files = [f for f in all_files if f.endswith(('.c', '.h')) and not f.startswith(IGNORED_PATHS)] +assert len(files) > 70, all(d in files for d in ('board/main.c', 'board/stm32f4/llbxcan.h', 'board/stm32h7/llfdcan.h', 'board/safety/safety_toyota.h')) + +for p in patterns: + mutations.append((random.choice(files), p, True)) + +@pytest.mark.parametrize("fn, patch, should_fail", mutations) +def test_misra_mutation(fn, patch, should_fail): + with tempfile.TemporaryDirectory() as tmp: + shutil.copytree(ROOT, tmp, dirs_exist_ok=True) + + # apply patch + if fn is not None: + r = os.system(f"cd {tmp} && sed -i '{patch}' {fn}") + assert r == 0 + + # run test + r = subprocess.run("tests/misra/test_misra.sh", cwd=tmp, shell=True) + failed = r.returncode != 0 + assert failed == should_fail + +if __name__ == "__main__": + pytest.main([__file__, "-n 8"]) diff --git a/panda/tests/read_flash_spi.py b/panda/tests/read_flash_spi.py new file mode 100644 index 0000000..133062b --- /dev/null +++ b/panda/tests/read_flash_spi.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +from panda import Panda, PandaDFU + +if __name__ == "__main__": + try: + from openpilot.system.hardware import HARDWARE + HARDWARE.recover_internal_panda() + Panda.wait_for_dfu(None, 5) + except Exception: + pass + + p = PandaDFU(None) + cfg = p.get_mcu_type().config + + def readmem(addr, length, fn): + print(f"reading {hex(addr)} {hex(length)} bytes to {fn}") + max_size = 255 + with open(fn, "wb") as f: + to_read = length + while to_read > 0: + l = min(to_read, max_size) + dat = p._handle.read(addr, l) + assert len(dat) == l + f.write(dat) + + to_read -= len(dat) + addr += len(dat) + + addr = cfg.bootstub_address + for i, sector_size in enumerate(cfg.sector_sizes): + readmem(addr, sector_size, f"sector_{i}.bin") + addr += sector_size diff --git a/panda/tests/read_st_flash.sh b/panda/tests/read_st_flash.sh new file mode 100644 index 0000000..ffcfd7b --- /dev/null +++ b/panda/tests/read_st_flash.sh @@ -0,0 +1,6 @@ +#!/bin/bash +rm -f /tmp/dump_bootstub +rm -f /tmp/dump_main +dfu-util -a 0 -s 0x08000000 -U /tmp/dump_bootstub +dfu-util -a 0 -s 0x08004000 -U /tmp/dump_main + diff --git a/panda/tests/read_winusb_descriptors.py b/panda/tests/read_winusb_descriptors.py new file mode 100644 index 0000000..5d311c9 --- /dev/null +++ b/panda/tests/read_winusb_descriptors.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# type: ignore +from panda import Panda +from hexdump import hexdump + +DEBUG = False + +if __name__ == "__main__": + p = Panda() + + length = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, 1) + print('Microsoft OS String Descriptor') + dat = p._handle.controlRead(Panda.REQUEST_IN, 0x06, 3 << 8 | 238, 0, length[0]) + if DEBUG: + print(f'LEN: {hex(length[0])}') + hexdump("".join(map(chr, dat))) + + ms_vendor_code = dat[16] + if DEBUG: + print(f'MS_VENDOR_CODE: {hex(length[0])}') + + print('\nMicrosoft Compatible ID Feature Descriptor') + length = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, 1) + if DEBUG: + print(f'LEN: {hex(length[0])}') + dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 4, length[0]) + hexdump("".join(map(chr, dat))) + + print('\nMicrosoft Extended Properties Feature Descriptor') + length = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, 1) + if DEBUG: + print(f'LEN: {hex(length[0])}') + dat = p._handle.controlRead(Panda.REQUEST_IN, ms_vendor_code, 0, 5, length[0]) + hexdump("".join(map(chr, dat))) diff --git a/panda/tests/reflash_internal_panda.py b/panda/tests/reflash_internal_panda.py new file mode 100644 index 0000000..c2ad9f8 --- /dev/null +++ b/panda/tests/reflash_internal_panda.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import time +from panda import Panda, PandaDFU + +class GPIO: + STM_RST_N = 124 + STM_BOOT0 = 134 + HUB_RST_N = 30 + + +def gpio_init(pin, output): + with open(f"/sys/class/gpio/gpio{pin}/direction", 'wb') as f: + f.write(b"out" if output else b"in") + +def gpio_set(pin, high): + with open(f"/sys/class/gpio/gpio{pin}/value", 'wb') as f: + f.write(b"1" if high else b"0") + + +if __name__ == "__main__": + for pin in (GPIO.STM_RST_N, GPIO.STM_BOOT0, GPIO.HUB_RST_N): + gpio_init(pin, True) + + # reset USB hub + gpio_set(GPIO.HUB_RST_N, 0) + time.sleep(0.5) + gpio_set(GPIO.HUB_RST_N, 1) + + # flash bootstub + print("resetting into DFU") + gpio_set(GPIO.STM_RST_N, 1) + gpio_set(GPIO.STM_BOOT0, 1) + time.sleep(1) + gpio_set(GPIO.STM_RST_N, 0) + gpio_set(GPIO.STM_BOOT0, 0) + time.sleep(1) + + print("flashing bootstub") + PandaDFU(None).recover() + + gpio_set(GPIO.STM_RST_N, 1) + time.sleep(0.5) + gpio_set(GPIO.STM_RST_N, 0) + time.sleep(1) + + print("flashing app") + p = Panda() + assert p.bootstub + p.flash() diff --git a/panda/tests/relay_test.py b/panda/tests/relay_test.py new file mode 100644 index 0000000..68789b1 --- /dev/null +++ b/panda/tests/relay_test.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +import time +from panda import Panda + +p = Panda() + +while True: + p.set_safety_mode(Panda.SAFETY_TOYOTA) + p.send_heartbeat() + print("ON") + time.sleep(1) + p.set_safety_mode(Panda.SAFETY_NOOUTPUT) + p.send_heartbeat() + print("OFF") + time.sleep(1) + diff --git a/panda/tests/restore_flash_spi.py b/panda/tests/restore_flash_spi.py new file mode 100644 index 0000000..c23b298 --- /dev/null +++ b/panda/tests/restore_flash_spi.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +from panda import Panda, PandaDFU, STBootloaderSPIHandle + +if __name__ == "__main__": + try: + from openpilot.system.hardware import HARDWARE + HARDWARE.recover_internal_panda() + Panda.wait_for_dfu(None, 5) + except Exception: + pass + + p = PandaDFU(None) + assert isinstance(p._handle, STBootloaderSPIHandle) + cfg = p.get_mcu_type().config + + print("restoring from backup...") + addr = cfg.bootstub_address + for i, sector_size in enumerate(cfg.sector_sizes): + print(f"- sector #{i}") + p._handle.erase_sector(i) + with open(f"sector_{i}.bin", "rb") as f: + dat = f.read() + assert len(dat) == sector_size + p._handle.program(addr, dat) + addr += len(dat) + + p.reset() diff --git a/panda/tests/safety/__init__.py b/panda/tests/safety/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panda/tests/safety/common.py b/panda/tests/safety/common.py new file mode 100644 index 0000000..bd7daf3 --- /dev/null +++ b/panda/tests/safety/common.py @@ -0,0 +1,1071 @@ +import os +import abc +import unittest +import importlib +import numpy as np +from collections.abc import Callable + +from opendbc.can.packer import CANPacker # pylint: disable=import-error +from panda import ALTERNATIVE_EXPERIENCE +from panda.tests.libpanda import libpanda_py + +MAX_WRONG_COUNTERS = 5 +MAX_SAMPLE_VALS = 6 +VEHICLE_SPEED_FACTOR = 100 + +MessageFunction = Callable[[float], libpanda_py.CANPacket] + +def sign_of(a): + return 1 if a > 0 else -1 + + +def make_msg(bus, addr, length=8, dat=None): + if dat is None: + dat = b'\x00' * length + return libpanda_py.make_CANPacket(addr, bus, dat) + + +class CANPackerPanda(CANPacker): + def make_can_msg_panda(self, name_or_addr, bus, values, fix_checksum=None): + msg = self.make_can_msg(name_or_addr, bus, values) + if fix_checksum is not None: + msg = fix_checksum(msg) + addr, _, dat, bus = msg + return libpanda_py.make_CANPacket(addr, bus, dat) + + +def add_regen_tests(cls): + """Dynamically adds regen tests for all user brake tests.""" + + # only rx/user brake tests, not brake command + found_tests = [func for func in dir(cls) if func.startswith("test_") and "user_brake" in func] + assert len(found_tests) >= 3, "Failed to detect known brake tests" + + for test in found_tests: + def _make_regen_test(brake_func): + def _regen_test(self): + # only for safety modes with a regen message + if self._user_regen_msg(0) is None: + raise unittest.SkipTest("Safety mode implements no _user_regen_msg") + + getattr(self, brake_func)(self._user_regen_msg, self.safety.get_regen_braking_prev) + return _regen_test + + setattr(cls, test.replace("brake", "regen"), _make_regen_test(test)) + + return cls + + +class PandaSafetyTestBase(unittest.TestCase): + safety: libpanda_py.Panda + + @classmethod + def setUpClass(cls): + if cls.__name__ == "PandaSafetyTestBase": + cls.safety = None + raise unittest.SkipTest + + def _reset_safety_hooks(self): + self.safety.set_safety_hooks(self.safety.get_current_safety_mode(), + self.safety.get_current_safety_param()) + + def _rx(self, msg): + return self.safety.safety_rx_hook(msg) + + def _tx(self, msg): + return self.safety.safety_tx_hook(msg) + + def _generic_limit_safety_check(self, msg_function: MessageFunction, min_allowed_value: float, max_allowed_value: float, + min_possible_value: float, max_possible_value: float, test_delta: float = 1, inactive_value: float = 0, + msg_allowed = True, additional_setup: Callable[[float], None] | None = None): + """ + Enforces that a signal within a message is only allowed to be sent within a specific range, min_allowed_value -> max_allowed_value. + Tests the range of min_possible_value -> max_possible_value with a delta of test_delta. + Message is also only allowed to be sent when controls_allowed is true, unless the value is equal to inactive_value. + Message is never allowed if msg_allowed is false, for example when stock longitudinal is enabled and you are sending acceleration requests. + additional_setup is used for extra setup before each _tx, ex: for setting the previous torque for rate limits + """ + + # Ensure that we at least test the allowed_value range + self.assertGreater(max_possible_value, max_allowed_value) + self.assertLessEqual(min_possible_value, min_allowed_value) + + for controls_allowed in [False, True]: + # enforce we don't skip over 0 or inactive + for v in np.concatenate((np.arange(min_possible_value, max_possible_value, test_delta), np.array([0, inactive_value]))): + v = round(v, 2) # floats might not hit exact boundary conditions without rounding + self.safety.set_controls_allowed(controls_allowed) + if additional_setup is not None: + additional_setup(v) + should_tx = controls_allowed and min_allowed_value <= v <= max_allowed_value + should_tx = (should_tx or v == inactive_value) and msg_allowed + self.assertEqual(self._tx(msg_function(v)), should_tx, (controls_allowed, should_tx, v)) + + def _common_measurement_test(self, msg_func: Callable, min_value: float, max_value: float, factor: float, + meas_min_func: Callable[[], int], meas_max_func: Callable[[], int]): + """Tests accurate measurement parsing, and that the struct is reset on safety mode init""" + for val in np.arange(min_value, max_value, 0.5): + for i in range(MAX_SAMPLE_VALS): + self.assertTrue(self._rx(msg_func(val + i * 0.1))) + + # assert close by one decimal place + self.assertAlmostEqual(meas_min_func() / factor, val, delta=0.1) + self.assertAlmostEqual(meas_max_func() / factor - 0.5, val, delta=0.1) + + # ensure sample_t is reset on safety init + self._reset_safety_hooks() + self.assertEqual(meas_min_func(), 0) + self.assertEqual(meas_max_func(), 0) + + +class GasInterceptorSafetyTest(PandaSafetyTestBase): + + INTERCEPTOR_THRESHOLD = 0 + + cnt_gas_cmd = 0 + cnt_user_gas = 0 + + packer: CANPackerPanda + + @classmethod + def setUpClass(cls): + if cls.__name__ == "GasInterceptorSafetyTest" or cls.__name__.endswith("Base"): + cls.safety = None + raise unittest.SkipTest + + def _interceptor_gas_cmd(self, gas: int): + values: dict[str, float | int] = {"COUNTER_PEDAL": self.__class__.cnt_gas_cmd & 0xF} + if gas > 0: + values["GAS_COMMAND"] = gas * 255. + values["GAS_COMMAND2"] = gas * 255. + self.__class__.cnt_gas_cmd += 1 + return self.packer.make_can_msg_panda("GAS_COMMAND", 0, values) + + def _interceptor_user_gas(self, gas: int): + values = {"INTERCEPTOR_GAS": gas, "INTERCEPTOR_GAS2": gas, + "COUNTER_PEDAL": self.__class__.cnt_user_gas} + self.__class__.cnt_user_gas += 1 + return self.packer.make_can_msg_panda("GAS_SENSOR", 0, values) + + # Skip non-interceptor user gas tests + def test_prev_gas(self): + pass + + def test_disengage_on_gas(self): + pass + + def test_alternative_experience_no_disengage_on_gas(self): + pass + + def test_prev_gas_interceptor(self): + self._rx(self._interceptor_user_gas(0x0)) + self.assertFalse(self.safety.get_gas_interceptor_prev()) + self._rx(self._interceptor_user_gas(0x1000)) + self.assertTrue(self.safety.get_gas_interceptor_prev()) + self._rx(self._interceptor_user_gas(0x0)) + + def test_disengage_on_gas_interceptor(self): + for g in range(0x1000): + self._rx(self._interceptor_user_gas(0)) + self.safety.set_controls_allowed(True) + self._rx(self._interceptor_user_gas(g)) + remain_enabled = g <= self.INTERCEPTOR_THRESHOLD + self.assertEqual(remain_enabled, self.safety.get_controls_allowed()) + self._rx(self._interceptor_user_gas(0)) + + def test_alternative_experience_no_disengage_on_gas_interceptor(self): + self.safety.set_controls_allowed(True) + self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS) + for g in range(0x1000): + self._rx(self._interceptor_user_gas(g)) + # Test we allow lateral, but not longitudinal + self.assertTrue(self.safety.get_controls_allowed()) + self.assertEqual(g <= self.INTERCEPTOR_THRESHOLD, self.safety.get_longitudinal_allowed()) + # Make sure we can re-gain longitudinal actuation + self._rx(self._interceptor_user_gas(0)) + self.assertTrue(self.safety.get_longitudinal_allowed()) + + def test_allow_engage_with_gas_interceptor_pressed(self): + self._rx(self._interceptor_user_gas(0x1000)) + self.safety.set_controls_allowed(1) + self._rx(self._interceptor_user_gas(0x1000)) + self.assertTrue(self.safety.get_controls_allowed()) + self._rx(self._interceptor_user_gas(0)) + + def test_gas_interceptor_safety_check(self): + for gas in np.arange(0, 4000, 100): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if controls_allowed: + send = True + else: + send = gas == 0 + self.assertEqual(send, self._tx(self._interceptor_gas_cmd(gas))) + + +class LongitudinalAccelSafetyTest(PandaSafetyTestBase, abc.ABC): + + MAX_ACCEL: float = 2.0 + MIN_ACCEL: float = -3.5 + INACTIVE_ACCEL: float = 0.0 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "LongitudinalAccelSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _accel_msg(self, accel: float): + pass + + def test_accel_limits_correct(self): + self.assertGreater(self.MAX_ACCEL, 0) + self.assertLess(self.MIN_ACCEL, 0) + + def test_accel_actuation_limits(self, stock_longitudinal=False): + limits = ((self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.DEFAULT), + (self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX)) + + for min_accel, max_accel, alternative_experience in limits: + # enforce we don't skip over 0 or inactive accel + for accel in np.concatenate((np.arange(min_accel - 1, max_accel + 1, 0.05), [0, self.INACTIVE_ACCEL])): + accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + self.safety.set_alternative_experience(alternative_experience) + if stock_longitudinal: + should_tx = False + else: + should_tx = controls_allowed and min_accel <= accel <= max_accel + should_tx = should_tx or accel == self.INACTIVE_ACCEL + self.assertEqual(should_tx, self._tx(self._accel_msg(accel))) + + +class LongitudinalGasBrakeSafetyTest(PandaSafetyTestBase, abc.ABC): + + MIN_BRAKE: int = 0 + MAX_BRAKE: int | None = None + MAX_POSSIBLE_BRAKE: int | None = None + + MIN_GAS: int = 0 + MAX_GAS: int | None = None + INACTIVE_GAS = 0 + MAX_POSSIBLE_GAS: int | None = None + + def test_gas_brake_limits_correct(self): + self.assertIsNotNone(self.MAX_POSSIBLE_BRAKE) + self.assertIsNotNone(self.MAX_POSSIBLE_GAS) + + self.assertGreater(self.MAX_BRAKE, self.MIN_BRAKE) + self.assertGreater(self.MAX_GAS, self.MIN_GAS) + + @abc.abstractmethod + def _send_gas_msg(self, gas: int): + pass + + @abc.abstractmethod + def _send_brake_msg(self, brake: int): + pass + + def test_brake_safety_check(self): + self._generic_limit_safety_check(self._send_brake_msg, self.MIN_BRAKE, self.MAX_BRAKE, 0, self.MAX_POSSIBLE_BRAKE, 1) + + def test_gas_safety_check(self): + self._generic_limit_safety_check(self._send_gas_msg, self.MIN_GAS, self.MAX_GAS, 0, self.MAX_POSSIBLE_GAS, 1, self.INACTIVE_GAS) + + +class TorqueSteeringSafetyTestBase(PandaSafetyTestBase, abc.ABC): + + MAX_RATE_UP = 0 + MAX_RATE_DOWN = 0 + MAX_TORQUE = 0 + MAX_RT_DELTA = 0 + RT_INTERVAL = 0 + + NO_STEER_REQ_BIT = False + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TorqueSteeringSafetyTestBase": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _torque_cmd_msg(self, torque, steer_req=1): + pass + + def _set_prev_torque(self, t): + self.safety.set_desired_torque_last(t) + self.safety.set_rt_torque_last(t) + + def test_steer_safety_check(self): + for enabled in [0, 1]: + for t in range(int(-self.MAX_TORQUE * 1.5), int(self.MAX_TORQUE * 1.5)): + self.safety.set_controls_allowed(enabled) + self._set_prev_torque(t) + if abs(t) > self.MAX_TORQUE or (not enabled and abs(t) > 0): + self.assertFalse(self._tx(self._torque_cmd_msg(t))) + else: + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + + def test_non_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + self._set_prev_torque(0) + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP))) + self._set_prev_torque(0) + self.assertTrue(self._tx(self._torque_cmd_msg(-self.MAX_RATE_UP))) + + self._set_prev_torque(0) + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP + 1))) + self.safety.set_controls_allowed(True) + self._set_prev_torque(0) + self.assertFalse(self._tx(self._torque_cmd_msg(-self.MAX_RATE_UP - 1))) + + def test_steer_req_bit(self): + """Asserts all torque safety modes check the steering request bit""" + if self.NO_STEER_REQ_BIT: + raise unittest.SkipTest("No steering request bit") + + self.safety.set_controls_allowed(True) + self._set_prev_torque(self.MAX_TORQUE) + + # Send torque successfully, then only drop the request bit and ensure it stays blocked + for _ in range(10): + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 1))) + + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 0))) + for _ in range(10): + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 1))) + + +class SteerRequestCutSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): + + @classmethod + def setUpClass(cls): + if cls.__name__ == "SteerRequestCutSafetyTest": + cls.safety = None + raise unittest.SkipTest + + # Safety around steering request bit mismatch tolerance + MIN_VALID_STEERING_FRAMES: int + MAX_INVALID_STEERING_FRAMES: int + MIN_VALID_STEERING_RT_INTERVAL: int + + def test_steer_req_bit_frames(self): + """ + Certain safety modes implement some tolerance on their steer request bits matching the + requested torque to avoid a steering fault or lockout and maintain torque. This tests: + - We can't cut torque for more than one frame + - We can't cut torque until at least the minimum number of matching steer_req messages + - We can always recover from violations if steer_req=1 + """ + + for min_valid_steer_frames in range(self.MIN_VALID_STEERING_FRAMES * 2): + # Reset match count and rt timer to allow cut (valid_steer_req_count, ts_steer_req_mismatch_last) + self.safety.init_tests() + self.safety.set_timer(self.MIN_VALID_STEERING_RT_INTERVAL) + + # Allow torque cut + self.safety.set_controls_allowed(True) + self._set_prev_torque(self.MAX_TORQUE) + for _ in range(min_valid_steer_frames): + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + + # should tx if we've sent enough valid frames, and we're not cutting torque for too many frames consecutively + should_tx = min_valid_steer_frames >= self.MIN_VALID_STEERING_FRAMES + for idx in range(self.MAX_INVALID_STEERING_FRAMES * 2): + tx = self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0)) + self.assertEqual(should_tx and idx < self.MAX_INVALID_STEERING_FRAMES, tx) + + # Keep blocking after one steer_req mismatch + for _ in range(100): + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) + + # Make sure we can recover + self.assertTrue(self._tx(self._torque_cmd_msg(0, steer_req=1))) + self._set_prev_torque(self.MAX_TORQUE) + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + + def test_steer_req_bit_multi_invalid(self): + """ + For safety modes allowing multiple consecutive invalid frames, this ensures that once a valid frame + is sent after an invalid frame (even without sending the max number of allowed invalid frames), + all counters are reset. + """ + for max_invalid_steer_frames in range(1, self.MAX_INVALID_STEERING_FRAMES * 2): + self.safety.init_tests() + self.safety.set_timer(self.MIN_VALID_STEERING_RT_INTERVAL) + + # Allow torque cut + self.safety.set_controls_allowed(True) + self._set_prev_torque(self.MAX_TORQUE) + for _ in range(self.MIN_VALID_STEERING_FRAMES): + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + + # Send partial amount of allowed invalid frames + for idx in range(max_invalid_steer_frames): + should_tx = idx < self.MAX_INVALID_STEERING_FRAMES + self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) + + # Send one valid frame, and subsequent invalid should now be blocked + self._set_prev_torque(self.MAX_TORQUE) + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + for _ in range(self.MIN_VALID_STEERING_FRAMES + 1): + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) + + def test_steer_req_bit_realtime(self): + """ + Realtime safety for cutting steer request bit. This tests: + - That we allow messages with mismatching steer request bit if time from last is >= MIN_VALID_STEERING_RT_INTERVAL + - That frame mismatch safety does not interfere with this test + """ + for rt_us in np.arange(self.MIN_VALID_STEERING_RT_INTERVAL - 50000, self.MIN_VALID_STEERING_RT_INTERVAL + 50000, 10000): + # Reset match count and rt timer (valid_steer_req_count, ts_steer_req_mismatch_last) + self.safety.init_tests() + + # Make sure valid_steer_req_count doesn't affect this test + self.safety.set_controls_allowed(True) + self._set_prev_torque(self.MAX_TORQUE) + for _ in range(self.MIN_VALID_STEERING_FRAMES): + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + + # Normally, sending MIN_VALID_STEERING_FRAMES valid frames should always allow + self.safety.set_timer(max(rt_us, 0)) + should_tx = rt_us >= self.MIN_VALID_STEERING_RT_INTERVAL + for _ in range(self.MAX_INVALID_STEERING_FRAMES): + self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) + + # Keep blocking after one steer_req mismatch + for _ in range(100): + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) + + # Make sure we can recover + self.assertTrue(self._tx(self._torque_cmd_msg(0, steer_req=1))) + self._set_prev_torque(self.MAX_TORQUE) + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) + + +class DriverTorqueSteeringSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): + + DRIVER_TORQUE_ALLOWANCE = 0 + DRIVER_TORQUE_FACTOR = 0 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "DriverTorqueSteeringSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _torque_driver_msg(self, torque): + pass + + def _reset_torque_driver_measurement(self, torque): + for _ in range(MAX_SAMPLE_VALS): + self._rx(self._torque_driver_msg(torque)) + + def test_non_realtime_limit_up(self): + self._reset_torque_driver_measurement(0) + super().test_non_realtime_limit_up() + + def test_against_torque_driver(self): + # Tests down limits and driver torque blending + self.safety.set_controls_allowed(True) + + # Cannot stay at MAX_TORQUE if above DRIVER_TORQUE_ALLOWANCE + for sign in [-1, 1]: + for driver_torque in np.arange(0, self.DRIVER_TORQUE_ALLOWANCE * 2, 1): + self._reset_torque_driver_measurement(-driver_torque * sign) + self._set_prev_torque(self.MAX_TORQUE * sign) + should_tx = abs(driver_torque) <= self.DRIVER_TORQUE_ALLOWANCE + self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE * sign))) + + # arbitrary high driver torque to ensure max steer torque is allowed + max_driver_torque = int(self.MAX_TORQUE / self.DRIVER_TORQUE_FACTOR + self.DRIVER_TORQUE_ALLOWANCE + 1) + + # spot check some individual cases + for sign in [-1, 1]: + # Ensure we wind down factor units for every unit above allowance + driver_torque = (self.DRIVER_TORQUE_ALLOWANCE + 10) * sign + torque_desired = (self.MAX_TORQUE - 10 * self.DRIVER_TORQUE_FACTOR) * sign + delta = 1 * sign + self._set_prev_torque(torque_desired) + self._reset_torque_driver_measurement(-driver_torque) + self.assertTrue(self._tx(self._torque_cmd_msg(torque_desired))) + self._set_prev_torque(torque_desired + delta) + self._reset_torque_driver_measurement(-driver_torque) + self.assertFalse(self._tx(self._torque_cmd_msg(torque_desired + delta))) + + # If we're well past the allowance, minimum wind down is MAX_RATE_DOWN + self._set_prev_torque(self.MAX_TORQUE * sign) + self._reset_torque_driver_measurement(-max_driver_torque * sign) + self.assertTrue(self._tx(self._torque_cmd_msg((self.MAX_TORQUE - self.MAX_RATE_DOWN) * sign))) + self._set_prev_torque(self.MAX_TORQUE * sign) + self._reset_torque_driver_measurement(-max_driver_torque * sign) + self.assertTrue(self._tx(self._torque_cmd_msg(0))) + self._set_prev_torque(self.MAX_TORQUE * sign) + self._reset_torque_driver_measurement(-max_driver_torque * sign) + self.assertFalse(self._tx(self._torque_cmd_msg((self.MAX_TORQUE - self.MAX_RATE_DOWN + 1) * sign))) + + def test_realtime_limits(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests() + self._set_prev_torque(0) + self._reset_torque_driver_measurement(0) + for t in np.arange(0, self.MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, self.MAX_RT_DELTA, 1): + t *= sign + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(self.RT_INTERVAL + 1) + self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA - 1)))) + self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) + + def test_reset_driver_torque_measurements(self): + # Tests that the driver torque measurement sample_t is reset on safety mode init + for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): + self.assertTrue(self._rx(self._torque_driver_msg(t))) + + self.assertNotEqual(self.safety.get_torque_driver_min(), 0) + self.assertNotEqual(self.safety.get_torque_driver_max(), 0) + + self._reset_safety_hooks() + self.assertEqual(self.safety.get_torque_driver_min(), 0) + self.assertEqual(self.safety.get_torque_driver_max(), 0) + + +class MotorTorqueSteeringSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): + + MAX_TORQUE_ERROR = 0 + TORQUE_MEAS_TOLERANCE = 0 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "MotorTorqueSteeringSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _torque_meas_msg(self, torque): + pass + + def _set_prev_torque(self, t): + super()._set_prev_torque(t) + self.safety.set_torque_meas(t, t) + + def test_torque_absolute_limits(self): + for controls_allowed in [True, False]: + for torque in np.arange(-self.MAX_TORQUE - 1000, self.MAX_TORQUE + 1000, self.MAX_RATE_UP): + self.safety.set_controls_allowed(controls_allowed) + self.safety.set_rt_torque_last(torque) + self.safety.set_torque_meas(torque, torque) + self.safety.set_desired_torque_last(torque - self.MAX_RATE_UP) + + if controls_allowed: + send = (-self.MAX_TORQUE <= torque <= self.MAX_TORQUE) + else: + send = torque == 0 + + self.assertEqual(send, self._tx(self._torque_cmd_msg(torque))) + + def test_non_realtime_limit_down(self): + self.safety.set_controls_allowed(True) + + torque_meas = self.MAX_TORQUE - self.MAX_TORQUE_ERROR - 50 + + self.safety.set_rt_torque_last(self.MAX_TORQUE) + self.safety.set_torque_meas(torque_meas, torque_meas) + self.safety.set_desired_torque_last(self.MAX_TORQUE) + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE - self.MAX_RATE_DOWN))) + + self.safety.set_rt_torque_last(self.MAX_TORQUE) + self.safety.set_torque_meas(torque_meas, torque_meas) + self.safety.set_desired_torque_last(self.MAX_TORQUE) + self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE - self.MAX_RATE_DOWN + 1))) + + def test_exceed_torque_sensor(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self._set_prev_torque(0) + for t in np.arange(0, self.MAX_TORQUE_ERROR + 2, 2): # step needs to be smaller than MAX_TORQUE_ERROR + t *= sign + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + + self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_TORQUE_ERROR + 2)))) + + def test_realtime_limit_up(self): + self.safety.set_controls_allowed(True) + + for sign in [-1, 1]: + self.safety.init_tests() + self._set_prev_torque(0) + for t in np.arange(0, self.MAX_RT_DELTA + 1, 1): + t *= sign + self.safety.set_torque_meas(t, t) + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) + + self._set_prev_torque(0) + for t in np.arange(0, self.MAX_RT_DELTA + 1, 1): + t *= sign + self.safety.set_torque_meas(t, t) + self.assertTrue(self._tx(self._torque_cmd_msg(t))) + + # Increase timer to update rt_torque_last + self.safety.set_timer(self.RT_INTERVAL + 1) + self.assertTrue(self._tx(self._torque_cmd_msg(sign * self.MAX_RT_DELTA))) + self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) + + def test_torque_measurements(self): + trq = 50 + for t in [trq, -trq, 0, 0, 0, 0]: + self._rx(self._torque_meas_msg(t)) + + max_range = range(trq, trq + self.TORQUE_MEAS_TOLERANCE + 1) + min_range = range(-(trq + self.TORQUE_MEAS_TOLERANCE), -trq + 1) + self.assertTrue(self.safety.get_torque_meas_min() in min_range) + self.assertTrue(self.safety.get_torque_meas_max() in max_range) + + max_range = range(self.TORQUE_MEAS_TOLERANCE + 1) + min_range = range(-(trq + self.TORQUE_MEAS_TOLERANCE), -trq + 1) + self._rx(self._torque_meas_msg(0)) + self.assertTrue(self.safety.get_torque_meas_min() in min_range) + self.assertTrue(self.safety.get_torque_meas_max() in max_range) + + max_range = range(self.TORQUE_MEAS_TOLERANCE + 1) + min_range = range(-self.TORQUE_MEAS_TOLERANCE, 0 + 1) + self._rx(self._torque_meas_msg(0)) + self.assertTrue(self.safety.get_torque_meas_min() in min_range) + self.assertTrue(self.safety.get_torque_meas_max() in max_range) + + def test_reset_torque_measurements(self): + # Tests that the torque measurement sample_t is reset on safety mode init + for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): + self.assertTrue(self._rx(self._torque_meas_msg(t))) + + self.assertNotEqual(self.safety.get_torque_meas_min(), 0) + self.assertNotEqual(self.safety.get_torque_meas_max(), 0) + + self._reset_safety_hooks() + self.assertEqual(self.safety.get_torque_meas_min(), 0) + self.assertEqual(self.safety.get_torque_meas_max(), 0) + + +class AngleSteeringSafetyTest(PandaSafetyTestBase): + + DEG_TO_CAN: float + ANGLE_RATE_BP: list[float] + ANGLE_RATE_UP: list[float] # windup limit + ANGLE_RATE_DOWN: list[float] # unwind limit + + @classmethod + def setUpClass(cls): + if cls.__name__ == "AngleSteeringSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _speed_msg(self, speed): + pass + + @abc.abstractmethod + def _angle_cmd_msg(self, angle: float, enabled: bool): + pass + + @abc.abstractmethod + def _angle_meas_msg(self, angle: float): + pass + + def _set_prev_desired_angle(self, t): + t = round(t * self.DEG_TO_CAN) + self.safety.set_desired_angle_last(t) + + def _reset_angle_measurement(self, angle): + for _ in range(MAX_SAMPLE_VALS): + self._rx(self._angle_meas_msg(angle)) + + def _reset_speed_measurement(self, speed): + for _ in range(MAX_SAMPLE_VALS): + self._rx(self._speed_msg(speed)) + + def test_vehicle_speed_measurements(self): + self._common_measurement_test(self._speed_msg, 0, 80, VEHICLE_SPEED_FACTOR, self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max) + + def test_steering_angle_measurements(self, max_angle=300): + self._common_measurement_test(self._angle_meas_msg, -max_angle, max_angle, self.DEG_TO_CAN, self.safety.get_angle_meas_min, self.safety.get_angle_meas_max) + + def test_angle_cmd_when_enabled(self, max_angle=300): + # when controls are allowed, angle cmd rate limit is enforced + speeds = [0., 1., 5., 10., 15., 50.] + angles = np.concatenate((np.arange(-max_angle, max_angle, 5), [0])) + for a in angles: + for s in speeds: + max_delta_up = np.interp(s, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) + max_delta_down = np.interp(s, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) + + # first test against false positives + self._reset_angle_measurement(a) + self._reset_speed_measurement(s) + + self._set_prev_desired_angle(a) + self.safety.set_controls_allowed(1) + + # Stay within limits + # Up + self.assertTrue(self._tx(self._angle_cmd_msg(a + sign_of(a) * max_delta_up, True))) + self.assertTrue(self.safety.get_controls_allowed()) + + # Don't change + self.assertTrue(self._tx(self._angle_cmd_msg(a, True))) + self.assertTrue(self.safety.get_controls_allowed()) + + # Down + self.assertTrue(self._tx(self._angle_cmd_msg(a - sign_of(a) * max_delta_down, True))) + self.assertTrue(self.safety.get_controls_allowed()) + + # Inject too high rates + # Up + self.assertFalse(self._tx(self._angle_cmd_msg(a + sign_of(a) * (max_delta_up + 1.1), True))) + + # Don't change + self.safety.set_controls_allowed(1) + self._set_prev_desired_angle(a) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertTrue(self._tx(self._angle_cmd_msg(a, True))) + self.assertTrue(self.safety.get_controls_allowed()) + + # Down + self.assertFalse(self._tx(self._angle_cmd_msg(a - sign_of(a) * (max_delta_down + 1.1), True))) + + # Check desired steer should be the same as steer angle when controls are off + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._angle_cmd_msg(a, False))) + + def test_angle_cmd_when_disabled(self): + # Tests that only angles close to the meas are allowed while + # steer actuation bit is 0, regardless of controls allowed. + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + + for steer_control_enabled in (True, False): + for angle_meas in np.arange(-90, 91, 10): + self._reset_angle_measurement(angle_meas) + + for angle_cmd in np.arange(-90, 91, 10): + self._set_prev_desired_angle(angle_cmd) + + # controls_allowed is checked if actuation bit is 1, else the angle must be close to meas (inactive) + should_tx = controls_allowed if steer_control_enabled else angle_cmd == angle_meas + self.assertEqual(should_tx, self._tx(self._angle_cmd_msg(angle_cmd, steer_control_enabled))) + + +class PandaSafetyTest(PandaSafetyTestBase): + TX_MSGS: list[list[int]] | None = None + SCANNED_ADDRS = [*range(0x800), # Entire 11-bit CAN address space + *range(0x18DA00F1, 0x18DB00F1, 0x100), # 29-bit UDS physical addressing + *range(0x18DB00F1, 0x18DC00F1, 0x100), # 29-bit UDS functional addressing + *range(0x3300, 0x3400), # Honda + 0x10400060, 0x104c006c] # GMLAN (exceptions, range/format unclear) + FWD_BLACKLISTED_ADDRS: dict[int, list[int]] = {} # {bus: [addr]} + FWD_BUS_LOOKUP: dict[int, int] = {} + + @classmethod + def setUpClass(cls): + if cls.__name__ == "PandaSafetyTest" or cls.__name__.endswith('Base'): + cls.safety = None + raise unittest.SkipTest + + # ***** standard tests for all safety modes ***** + + def test_tx_msg_in_scanned_range(self): + # the relay malfunction, fwd hook, and spam can tests don't exhaustively + # scan the entire 29-bit address space, only some known important ranges + # make sure SCANNED_ADDRS stays up to date with car port TX_MSGS; new + # model ports should expand the range if needed + for msg in self.TX_MSGS: + self.assertTrue(msg[0] in self.SCANNED_ADDRS, f"{msg[0]=:#x}") + + def test_fwd_hook(self): + # some safety modes don't forward anything, while others blacklist msgs + for bus in range(3): + for addr in self.SCANNED_ADDRS: + # assume len 8 + fwd_bus = self.FWD_BUS_LOOKUP.get(bus, -1) + if bus in self.FWD_BLACKLISTED_ADDRS and addr in self.FWD_BLACKLISTED_ADDRS[bus]: + fwd_bus = -1 + self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(bus, addr), f"{addr=:#x} from {bus=} to {fwd_bus=}") + + def test_spam_can_buses(self): + for bus in range(4): + for addr in self.SCANNED_ADDRS: + if [addr, bus] not in self.TX_MSGS: + self.assertFalse(self._tx(make_msg(bus, addr, 8)), f"allowed TX {addr=} {bus=}") + + def test_default_controls_not_allowed(self): + self.assertFalse(self.safety.get_controls_allowed()) + + def test_manually_enable_controls_allowed(self): + self.safety.set_controls_allowed(1) + self.assertTrue(self.safety.get_controls_allowed()) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_tx_hook_on_wrong_safety_mode(self): + files = os.listdir(os.path.dirname(os.path.realpath(__file__))) + test_files = [f for f in files if f.startswith("test_") and f.endswith(".py")] + + current_test = self.__class__.__name__ + + all_tx = [] + for tf in test_files: + test = importlib.import_module("panda.tests.safety."+tf[:-3]) + for attr in dir(test): + if attr.startswith("Test") and attr != current_test: + tc = getattr(test, attr) + tx = tc.TX_MSGS + if tx is not None and not attr.endswith('Base'): + # No point in comparing different Tesla safety modes + if 'Tesla' in attr and 'Tesla' in current_test: + continue + # No point in comparing to ALLOUTPUT which allows all messages + if attr.startswith('TestAllOutput'): + continue + if attr.startswith('TestToyota') and current_test.startswith('TestToyota'): + continue + if attr.startswith('TestSubaruGen') and current_test.startswith('TestSubaruGen'): + continue + if attr.startswith('TestSubaruPreglobal') and current_test.startswith('TestSubaruPreglobal'): + continue + if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}): + continue + if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmSdgmSafety', 'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety'}): + continue + if attr.startswith('TestFord') and current_test.startswith('TestFord'): + continue + if attr.startswith('TestHyundaiCanfd') and current_test.startswith('TestHyundaiCanfd'): + continue + if {attr, current_test}.issubset({'TestVolkswagenMqbSafety', 'TestVolkswagenMqbStockSafety', 'TestVolkswagenMqbLongSafety'}): + continue + + # overlapping TX addrs, but they're not actuating messages for either car + if attr == 'TestHyundaiCanfdHDA2LongEV' and current_test.startswith('TestToyota'): + tx = list(filter(lambda m: m[0] not in [0x160, ], tx)) + + # Volkswagen MQB longitudinal actuating message overlaps with the Subaru lateral actuating message + if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestSubaru'): + tx = list(filter(lambda m: m[0] not in [0x122, ], tx)) + + # Volkswagen MQB and Honda Nidec ACC HUD messages overlap + if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestHondaNidec'): + tx = list(filter(lambda m: m[0] not in [0x30c, ], tx)) + + # Volkswagen MQB and Honda Bosch Radarless ACC HUD messages overlap + if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestHondaBoschRadarless'): + tx = list(filter(lambda m: m[0] not in [0x30c, ], tx)) + + # TODO: Temporary, should be fixed in panda firmware, safety_honda.h + if attr.startswith('TestHonda'): + # exceptions for common msgs across different hondas + tx = list(filter(lambda m: m[0] not in [0x1FA, 0x30C, 0x33D, 0x33DB], tx)) + all_tx.append([[m[0], m[1], attr] for m in tx]) + + # make sure we got all the msgs + self.assertTrue(len(all_tx) >= len(test_files)-1) + + for tx_msgs in all_tx: + for addr, bus, test_name in tx_msgs: + msg = make_msg(bus, addr) + self.safety.set_controls_allowed(1) + # TODO: this should be blocked + if current_test in ["TestNissanSafety", "TestNissanSafetyAltEpsBus", "TestNissanLeafSafety"] and [addr, bus] in self.TX_MSGS: + continue + self.assertFalse(self._tx(msg), f"transmit of {addr=:#x} {bus=} from {test_name} during {current_test} was allowed") + + +@add_regen_tests +class PandaCarSafetyTest(PandaSafetyTest): + STANDSTILL_THRESHOLD: float | None = None + GAS_PRESSED_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS: dict[int, tuple[int, ...]] | None = None + + @classmethod + def setUpClass(cls): + if cls.__name__ == "PandaCarSafetyTest" or cls.__name__.endswith('Base'): + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _user_brake_msg(self, brake): + pass + + def _user_regen_msg(self, regen): + pass + + @abc.abstractmethod + def _speed_msg(self, speed): + pass + + # Safety modes can override if vehicle_moving is driven by a different message + def _vehicle_moving_msg(self, speed: float): + return self._speed_msg(speed) + + @abc.abstractmethod + def _user_gas_msg(self, gas): + pass + + @abc.abstractmethod + def _pcm_status_msg(self, enable): + pass + + # ***** standard tests for all car-specific safety modes ***** + + def test_relay_malfunction(self): + # each car has an addr that is used to detect relay malfunction + # if that addr is seen on specified bus, triggers the relay malfunction + # protection logic: both tx_hook and fwd_hook are expected to return failure + self.assertFalse(self.safety.get_relay_malfunction()) + for bus in range(3): + for addr in self.SCANNED_ADDRS: + self.safety.set_relay_malfunction(False) + self._rx(make_msg(bus, addr, 8)) + should_relay_malfunction = addr in self.RELAY_MALFUNCTION_ADDRS.get(bus, ()) + self.assertEqual(should_relay_malfunction, self.safety.get_relay_malfunction(), (bus, addr)) + + # test relay malfunction protection logic + self.safety.set_relay_malfunction(True) + for bus in range(3): + for addr in self.SCANNED_ADDRS: + self.assertFalse(self._tx(make_msg(bus, addr, 8))) + self.assertEqual(-1, self.safety.safety_fwd_hook(bus, addr)) + + def test_prev_gas(self): + self.assertFalse(self.safety.get_gas_pressed_prev()) + for pressed in [self.GAS_PRESSED_THRESHOLD + 1, 0]: + self._rx(self._user_gas_msg(pressed)) + self.assertEqual(bool(pressed), self.safety.get_gas_pressed_prev()) + + def test_allow_engage_with_gas_pressed(self): + self._rx(self._user_gas_msg(1)) + self.safety.set_controls_allowed(True) + self._rx(self._user_gas_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + self._rx(self._user_gas_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disengage_on_gas(self): + self._rx(self._user_gas_msg(0)) + self.safety.set_controls_allowed(True) + self._rx(self._user_gas_msg(self.GAS_PRESSED_THRESHOLD + 1)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_alternative_experience_no_disengage_on_gas(self): + self._rx(self._user_gas_msg(0)) + self.safety.set_controls_allowed(True) + self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS) + self._rx(self._user_gas_msg(self.GAS_PRESSED_THRESHOLD + 1)) + # Test we allow lateral, but not longitudinal + self.assertTrue(self.safety.get_controls_allowed()) + self.assertFalse(self.safety.get_longitudinal_allowed()) + # Make sure we can re-gain longitudinal actuation + self._rx(self._user_gas_msg(0)) + self.assertTrue(self.safety.get_longitudinal_allowed()) + + def test_prev_user_brake(self, _user_brake_msg=None, get_brake_pressed_prev=None): + if _user_brake_msg is None: + _user_brake_msg = self._user_brake_msg + get_brake_pressed_prev = self.safety.get_brake_pressed_prev + + self.assertFalse(get_brake_pressed_prev()) + for pressed in [True, False]: + self._rx(_user_brake_msg(not pressed)) + self.assertEqual(not pressed, get_brake_pressed_prev()) + self._rx(_user_brake_msg(pressed)) + self.assertEqual(pressed, get_brake_pressed_prev()) + + def test_enable_control_allowed_from_cruise(self): + self._rx(self._pcm_status_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + self._rx(self._pcm_status_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_disable_control_allowed_from_cruise(self): + self.safety.set_controls_allowed(1) + self._rx(self._pcm_status_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_cruise_engaged_prev(self): + for engaged in [True, False]: + self._rx(self._pcm_status_msg(engaged)) + self.assertEqual(engaged, self.safety.get_cruise_engaged_prev()) + self._rx(self._pcm_status_msg(not engaged)) + self.assertEqual(not engaged, self.safety.get_cruise_engaged_prev()) + + def test_allow_user_brake_at_zero_speed(self, _user_brake_msg=None, get_brake_pressed_prev=None): + if _user_brake_msg is None: + _user_brake_msg = self._user_brake_msg + + # Brake was already pressed + self._rx(self._vehicle_moving_msg(0)) + self._rx(_user_brake_msg(1)) + self.safety.set_controls_allowed(1) + self._rx(_user_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertTrue(self.safety.get_longitudinal_allowed()) + self._rx(_user_brake_msg(0)) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertTrue(self.safety.get_longitudinal_allowed()) + # rising edge of brake should disengage + self._rx(_user_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + self.assertFalse(self.safety.get_longitudinal_allowed()) + self._rx(_user_brake_msg(0)) # reset no brakes + + def test_not_allow_user_brake_when_moving(self, _user_brake_msg=None, get_brake_pressed_prev=None): + if _user_brake_msg is None: + _user_brake_msg = self._user_brake_msg + + # Brake was already pressed + self._rx(_user_brake_msg(1)) + self.safety.set_controls_allowed(1) + self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD)) + self._rx(_user_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + self.assertTrue(self.safety.get_longitudinal_allowed()) + self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD + 1)) + self._rx(_user_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + self.assertFalse(self.safety.get_longitudinal_allowed()) + self._rx(self._vehicle_moving_msg(0)) + + def test_vehicle_moving(self): + self.assertFalse(self.safety.get_vehicle_moving()) + + # not moving + self._rx(self._vehicle_moving_msg(0)) + self.assertFalse(self.safety.get_vehicle_moving()) + + # speed is at threshold + self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD)) + self.assertFalse(self.safety.get_vehicle_moving()) + + # past threshold + self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD + 1)) + self.assertTrue(self.safety.get_vehicle_moving()) + + def test_safety_tick(self): + self.safety.set_timer(int(2e6)) + self.safety.set_controls_allowed(True) + self.safety.safety_tick_current_safety_config() + self.assertFalse(self.safety.get_controls_allowed()) + self.assertFalse(self.safety.safety_config_valid()) diff --git a/panda/tests/safety/hyundai_common.py b/panda/tests/safety/hyundai_common.py new file mode 100644 index 0000000..da18671 --- /dev/null +++ b/panda/tests/safety/hyundai_common.py @@ -0,0 +1,157 @@ +import unittest + +import panda.tests.safety.common as common +from panda.tests.libpanda import libpanda_py +from panda.tests.safety.common import make_msg + + +class Buttons: + NONE = 0 + RESUME = 1 + SET = 2 + CANCEL = 4 + + +PREV_BUTTON_SAMPLES = 8 +ENABLE_BUTTONS = (Buttons.RESUME, Buttons.SET, Buttons.CANCEL) + + +class HyundaiButtonBase: + # pylint: disable=no-member,abstract-method + BUTTONS_TX_BUS = 0 # tx on this bus, rx on 0 + SCC_BUS = 0 # rx on this bus + + def test_button_sends(self): + """ + Only RES and CANCEL buttons are allowed + - RES allowed while controls allowed + - CANCEL allowed while cruise is enabled + """ + self.safety.set_controls_allowed(0) + self.assertFalse(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS))) + self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS))) + + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS))) + self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS))) + + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL, bus=self.BUTTONS_TX_BUS))) + + def test_enable_control_allowed_from_cruise(self): + """ + Hyundai non-longitudinal only enables on PCM rising edge and recent button press. Tests PCM enabling with: + - disallowed: No buttons + - disallowed: Buttons that don't enable cruise + - allowed: Buttons that do enable cruise + - allowed: Main button with all above combinations + """ + for main_button in (0, 1): + for btn in range(8): + for _ in range(PREV_BUTTON_SAMPLES): # reset + self._rx(self._button_msg(Buttons.NONE)) + + self._rx(self._pcm_status_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + self._rx(self._button_msg(btn, main_button=main_button)) + self._rx(self._pcm_status_msg(True)) + controls_allowed = btn in ENABLE_BUTTONS or main_button + self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) + + def test_sampling_cruise_buttons(self): + """ + Test that we allow controls on recent button press, but not as button leaves sliding window + """ + self._rx(self._button_msg(Buttons.SET)) + for i in range(2 * PREV_BUTTON_SAMPLES): + self._rx(self._pcm_status_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + self._rx(self._pcm_status_msg(True)) + controls_allowed = i < PREV_BUTTON_SAMPLES + self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) + self._rx(self._button_msg(Buttons.NONE)) + + +class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest): + # pylint: disable=no-member,abstract-method + + DISABLED_ECU_UDS_MSG: tuple[int, int] + DISABLED_ECU_ACTUATION_MSG: tuple[int, int] + + @classmethod + def setUpClass(cls): + if cls.__name__ == "HyundaiLongitudinalBase": + cls.safety = None + raise unittest.SkipTest + + # override these tests from PandaCarSafetyTest, hyundai longitudinal uses button enable + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_sampling_cruise_buttons(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def test_button_sends(self): + pass + + def _pcm_status_msg(self, enable): + raise Exception + + def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): + raise NotImplementedError + + def test_set_resume_buttons(self): + """ + SET and RESUME enter controls allowed on their falling edge. + """ + for btn_prev in range(8): + for btn_cur in range(8): + self._rx(self._button_msg(Buttons.NONE)) + self.safety.set_controls_allowed(0) + for _ in range(10): + self._rx(self._button_msg(btn_prev)) + self.assertFalse(self.safety.get_controls_allowed()) + + # should enter controls allowed on falling edge and not transitioning to cancel + should_enable = btn_cur != btn_prev and \ + btn_cur != Buttons.CANCEL and \ + btn_prev in (Buttons.RESUME, Buttons.SET) + + self._rx(self._button_msg(btn_cur)) + self.assertEqual(should_enable, self.safety.get_controls_allowed()) + + def test_cancel_button(self): + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(Buttons.CANCEL)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_tester_present_allowed(self): + """ + Ensure tester present diagnostic message is allowed to keep ECU knocked out + for longitudinal control. + """ + + addr, bus = self.DISABLED_ECU_UDS_MSG + tester_present = libpanda_py.make_CANPacket(addr, bus, b"\x02\x3E\x80\x00\x00\x00\x00\x00") + self.assertTrue(self._tx(tester_present)) + + not_tester_present = libpanda_py.make_CANPacket(addr, bus, b"\x03\xAA\xAA\x00\x00\x00\x00\x00") + self.assertFalse(self._tx(not_tester_present)) + + def test_disabled_ecu_alive(self): + """ + If the ECU knockout failed, make sure the relay malfunction is shown + """ + + addr, bus = self.DISABLED_ECU_ACTUATION_MSG + self.assertFalse(self.safety.get_relay_malfunction()) + self._rx(make_msg(bus, addr, 8)) + self.assertTrue(self.safety.get_relay_malfunction()) + diff --git a/panda/tests/safety/test.sh b/panda/tests/safety/test.sh new file mode 100644 index 0000000..13703b2 --- /dev/null +++ b/panda/tests/safety/test.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +# reset coverage data and generate gcc note file +rm -f ../libpanda/*.gcda +scons -j$(nproc) -D --coverage + +# run safety tests and generate coverage data +HW_TYPES=( 6 9 ) +for hw_type in "${HW_TYPES[@]}"; do + echo "Testing HW_TYPE: $hw_type" + HW_TYPE=$hw_type pytest test_*.py +done + +# generate and open report +if [ "$1" == "--report" ]; then + geninfo ../libpanda/ -o coverage.info + genhtml coverage.info -o coverage-out + sensible-browser coverage-out/index.html +fi + +# test coverage +GCOV_OUTPUT=$(gcov -n ../libpanda/panda.c) +INCOMPLETE_COVERAGE=$(echo "$GCOV_OUTPUT" | paste -s -d' \n' | grep -E "File.*(safety\/safety_.*)|(safety)\.h" | grep -v "100.00%" || true) +if [ -n "$INCOMPLETE_COVERAGE" ]; then + echo "FAILED: Some files have less than 100% coverage:" + echo "$INCOMPLETE_COVERAGE" + exit 1 +else + echo "SUCCESS: All checked files have 100% coverage!" +fi diff --git a/panda/tests/safety/test_body.py b/panda/tests/safety/test_body.py new file mode 100644 index 0000000..d23c09f --- /dev/null +++ b/panda/tests/safety/test_body.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import unittest + +import panda.tests.safety.common as common + +from panda import Panda +from panda.tests.libpanda import libpanda_py +from panda.tests.safety.common import CANPackerPanda + + +class TestBody(common.PandaSafetyTest): + TX_MSGS = [[0x250, 0], [0x251, 0], [0x350, 0], [0x351, 0], + [0x1, 0], [0x1, 1], [0x1, 2], [0x1, 3]] + + def setUp(self): + self.packer = CANPackerPanda("comma_body") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_BODY, 0) + self.safety.init_tests() + + def _motors_data_msg(self, speed_l, speed_r): + values = {"SPEED_L": speed_l, "SPEED_R": speed_r} + return self.packer.make_can_msg_panda("MOTORS_DATA", 0, values) + + def _torque_cmd_msg(self, torque_l, torque_r): + values = {"TORQUE_L": torque_l, "TORQUE_R": torque_r} + return self.packer.make_can_msg_panda("TORQUE_CMD", 0, values) + + def _knee_torque_cmd_msg(self, torque_l, torque_r): + values = {"TORQUE_L": torque_l, "TORQUE_R": torque_r} + return self.packer.make_can_msg_panda("KNEE_TORQUE_CMD", 0, values) + + def _max_motor_rpm_cmd_msg(self, max_rpm_l, max_rpm_r): + values = {"MAX_RPM_L": max_rpm_l, "MAX_RPM_R": max_rpm_r} + return self.packer.make_can_msg_panda("MAX_MOTOR_RPM_CMD", 0, values) + + def test_rx_hook(self): + self.assertFalse(self.safety.get_controls_allowed()) + self.assertFalse(self.safety.get_vehicle_moving()) + + # controls allowed when we get MOTORS_DATA message + self.assertTrue(self._rx(self._torque_cmd_msg(0, 0))) + self.assertTrue(self.safety.get_vehicle_moving()) # always moving + self.assertFalse(self.safety.get_controls_allowed()) + + self.assertTrue(self._rx(self._motors_data_msg(0, 0))) + self.assertTrue(self.safety.get_vehicle_moving()) # always moving + self.assertTrue(self.safety.get_controls_allowed()) + + def test_tx_hook(self): + self.assertFalse(self._tx(self._torque_cmd_msg(0, 0))) + self.assertFalse(self._tx(self._knee_torque_cmd_msg(0, 0))) + self.safety.set_controls_allowed(True) + self.assertTrue(self._tx(self._torque_cmd_msg(0, 0))) + self.assertTrue(self._tx(self._knee_torque_cmd_msg(0, 0))) + + def test_can_flasher(self): + # CAN flasher always allowed + self.safety.set_controls_allowed(False) + self.assertTrue(self._tx(common.make_msg(0, 0x1, 8))) + + # 0xdeadfaceU enters CAN flashing mode for base & knee + for addr in (0x250, 0x350): + self.assertTrue(self._tx(common.make_msg(0, addr, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0\x0a'))) + self.assertFalse(self._tx(common.make_msg(0, addr, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0'))) # not correct data/len + self.assertFalse(self._tx(common.make_msg(0, addr + 1, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0\x0a'))) # wrong address + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_chrysler.py b/panda/tests/safety/test_chrysler.py new file mode 100644 index 0000000..5bbb6dd --- /dev/null +++ b/panda/tests/safety/test_chrysler.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + + +class TestChryslerSafety(common.PandaCarSafetyTest, common.MotorTorqueSteeringSafetyTest): + TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0]] + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS = {0: (0x292,)} + FWD_BLACKLISTED_ADDRS = {2: [0x292, 0x2A6]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + MAX_RATE_UP = 3 + MAX_RATE_DOWN = 3 + MAX_TORQUE = 261 + MAX_RT_DELTA = 112 + RT_INTERVAL = 250000 + MAX_TORQUE_ERROR = 80 + + LKAS_ACTIVE_VALUE = 1 + + DAS_BUS = 0 + + def setUp(self): + self.packer = CANPackerPanda("chrysler_pacifica_2017_hybrid_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_CHRYSLER, 0) + self.safety.init_tests() + + def _button_msg(self, cancel=False, resume=False): + values = {"ACC_Cancel": cancel, "ACC_Resume": resume} + return self.packer.make_can_msg_panda("CRUISE_BUTTONS", self.DAS_BUS, values) + + def _pcm_status_msg(self, enable): + values = {"ACC_ACTIVE": enable} + return self.packer.make_can_msg_panda("DAS_3", self.DAS_BUS, values) + + def _speed_msg(self, speed): + values = {"SPEED_LEFT": speed, "SPEED_RIGHT": speed} + return self.packer.make_can_msg_panda("SPEED_1", 0, values) + + def _user_gas_msg(self, gas): + values = {"Accelerator_Position": gas} + return self.packer.make_can_msg_panda("ECM_5", 0, values) + + def _user_brake_msg(self, brake): + values = {"Brake_Pedal_State": 1 if brake else 0} + return self.packer.make_can_msg_panda("ESP_1", 0, values) + + def _torque_meas_msg(self, torque): + values = {"EPS_TORQUE_MOTOR": torque} + return self.packer.make_can_msg_panda("EPS_2", 0, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"STEERING_TORQUE": torque, "LKAS_CONTROL_BIT": self.LKAS_ACTIVE_VALUE if steer_req else 0} + return self.packer.make_can_msg_panda("LKAS_COMMAND", 0, values) + + def test_buttons(self): + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + + # resume only while controls allowed + self.assertEqual(controls_allowed, self._tx(self._button_msg(resume=True))) + + # can always cancel + self.assertTrue(self._tx(self._button_msg(cancel=True))) + + # only one button at a time + self.assertFalse(self._tx(self._button_msg(cancel=True, resume=True))) + self.assertFalse(self._tx(self._button_msg(cancel=False, resume=False))) + + +class TestChryslerRamDTSafety(TestChryslerSafety): + TX_MSGS = [[0xB1, 2], [0xA6, 0], [0xFA, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0xA6,)} + FWD_BLACKLISTED_ADDRS = {2: [0xA6, 0xFA]} + + MAX_RATE_UP = 6 + MAX_RATE_DOWN = 6 + MAX_TORQUE = 350 + + DAS_BUS = 2 + + LKAS_ACTIVE_VALUE = 2 + + def setUp(self): + self.packer = CANPackerPanda("chrysler_ram_dt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_CHRYSLER, Panda.FLAG_CHRYSLER_RAM_DT) + self.safety.init_tests() + + def _speed_msg(self, speed): + values = {"Vehicle_Speed": speed} + return self.packer.make_can_msg_panda("ESP_8", 0, values) + +class TestChryslerRamHDSafety(TestChryslerSafety): + TX_MSGS = [[0x275, 0], [0x276, 0], [0x23A, 2]] + RELAY_MALFUNCTION_ADDRS = {0: (0x276,)} + FWD_BLACKLISTED_ADDRS = {2: [0x275, 0x276]} + + MAX_TORQUE = 361 + MAX_RATE_UP = 14 + MAX_RATE_DOWN = 14 + MAX_RT_DELTA = 182 + + DAS_BUS = 2 + + LKAS_ACTIVE_VALUE = 2 + + def setUp(self): + self.packer = CANPackerPanda("chrysler_ram_hd_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_CHRYSLER, Panda.FLAG_CHRYSLER_RAM_HD) + self.safety.init_tests() + + def _speed_msg(self, speed): + values = {"Vehicle_Speed": speed} + return self.packer.make_can_msg_panda("ESP_8", 0, values) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_defaults.py b/panda/tests/safety/test_defaults.py new file mode 100644 index 0000000..81bafee --- /dev/null +++ b/panda/tests/safety/test_defaults.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +import unittest + +import panda.tests.safety.common as common + +from panda import Panda +from panda.tests.libpanda import libpanda_py + + +class TestDefaultRxHookBase(common.PandaSafetyTest): + def test_rx_hook(self): + # default rx hook allows all msgs + for bus in range(4): + for addr in self.SCANNED_ADDRS: + self.assertTrue(self._rx(common.make_msg(bus, addr, 8)), f"failed RX {addr=}") + + +class TestNoOutput(TestDefaultRxHookBase): + TX_MSGS = [] + + def setUp(self): + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_NOOUTPUT, 0) + self.safety.init_tests() + + +class TestSilent(TestNoOutput): + """SILENT uses same hooks as NOOUTPUT""" + + def setUp(self): + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_SILENT, 0) + self.safety.init_tests() + + +class TestAllOutput(TestDefaultRxHookBase): + # Allow all messages + TX_MSGS = [[addr, bus] for addr in common.PandaSafetyTest.SCANNED_ADDRS + for bus in range(4)] + + def setUp(self): + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_ALLOUTPUT, 0) + self.safety.init_tests() + + def test_spam_can_buses(self): + # asserts tx allowed for all scanned addrs + for bus in range(4): + for addr in self.SCANNED_ADDRS: + should_tx = [addr, bus] in self.TX_MSGS + self.assertEqual(should_tx, self._tx(common.make_msg(bus, addr, 8)), f"allowed TX {addr=} {bus=}") + + def test_default_controls_not_allowed(self): + # controls always allowed + self.assertTrue(self.safety.get_controls_allowed()) + + def test_tx_hook_on_wrong_safety_mode(self): + # No point, since we allow all messages + pass + + +class TestAllOutputPassthrough(TestAllOutput): + FWD_BLACKLISTED_ADDRS = {} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + def setUp(self): + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_ALLOUTPUT, 1) + self.safety.init_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_elm327.py b/panda/tests/safety/test_elm327.py new file mode 100644 index 0000000..f133b2e --- /dev/null +++ b/panda/tests/safety/test_elm327.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import unittest + +import panda.tests.safety.common as common + +from panda import DLC_TO_LEN, Panda +from panda.tests.libpanda import libpanda_py +from panda.tests.safety.test_defaults import TestDefaultRxHookBase + +GM_CAMERA_DIAG_ADDR = 0x24B + + +class TestElm327(TestDefaultRxHookBase): + TX_MSGS = [[addr, bus] for addr in [GM_CAMERA_DIAG_ADDR, *range(0x600, 0x800), + *range(0x18DA00F1, 0x18DB00F1, 0x100), # 29-bit UDS physical addressing + *[0x18DB33F1], # 29-bit UDS functional address + ] for bus in range(4)] + + def setUp(self): + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_ELM327, 0) + self.safety.init_tests() + + def test_tx_hook(self): + # ensure we can transmit arbitrary data on allowed addresses + for bus in range(4): + for addr in self.SCANNED_ADDRS: + should_tx = [addr, bus] in self.TX_MSGS + self.assertEqual(should_tx, self._tx(common.make_msg(bus, addr, 8))) + + # ELM only allows 8 byte UDS/KWP messages under ISO 15765-4 + for msg_len in DLC_TO_LEN: + should_tx = msg_len == 8 + self.assertEqual(should_tx, self._tx(common.make_msg(0, 0x700, msg_len))) + + # TODO: perform this check for all addresses + # 4 to 15 are reserved ISO-TP frame types (https://en.wikipedia.org/wiki/ISO_15765-2) + for byte in range(0xff): + should_tx = (byte >> 4) <= 3 + self.assertEqual(should_tx, self._tx(common.make_msg(0, GM_CAMERA_DIAG_ADDR, dat=bytes([byte] * 8)))) + + def test_tx_hook_on_wrong_safety_mode(self): + # No point, since we allow many diagnostic addresses + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_ford.py b/panda/tests/safety/test_ford.py new file mode 100644 index 0000000..1be3a27 --- /dev/null +++ b/panda/tests/safety/test_ford.py @@ -0,0 +1,476 @@ +#!/usr/bin/env python3 +import numpy as np +import random +import unittest + +import panda.tests.safety.common as common + +from panda import Panda +from panda.tests.libpanda import libpanda_py +from panda.tests.safety.common import CANPackerPanda + +MSG_EngBrakeData = 0x165 # RX from PCM, for driver brake pedal and cruise state +MSG_EngVehicleSpThrottle = 0x204 # RX from PCM, for driver throttle input +MSG_BrakeSysFeatures = 0x415 # RX from ABS, for vehicle speed +MSG_EngVehicleSpThrottle2 = 0x202 # RX from PCM, for second vehicle speed +MSG_Yaw_Data_FD1 = 0x91 # RX from RCM, for yaw rate +MSG_Steering_Data_FD1 = 0x083 # TX by OP, various driver switches and LKAS/CC buttons +MSG_ACCDATA = 0x186 # TX by OP, ACC controls +MSG_ACCDATA_3 = 0x18A # TX by OP, ACC/TJA user interface +MSG_Lane_Assist_Data1 = 0x3CA # TX by OP, Lane Keep Assist +MSG_LateralMotionControl = 0x3D3 # TX by OP, Lateral Control message +MSG_LateralMotionControl2 = 0x3D6 # TX by OP, alternate Lateral Control message +MSG_IPMA_Data = 0x3D8 # TX by OP, IPMA and LKAS user interface + + +def checksum(msg): + addr, t, dat, bus = msg + ret = bytearray(dat) + + if addr == MSG_Yaw_Data_FD1: + chksum = dat[0] + dat[1] # VehRol_W_Actl + chksum += dat[2] + dat[3] # VehYaw_W_Actl + chksum += dat[5] # VehRollYaw_No_Cnt + chksum += dat[6] >> 6 # VehRolWActl_D_Qf + chksum += (dat[6] >> 4) & 0x3 # VehYawWActl_D_Qf + chksum = 0xff - (chksum & 0xff) + ret[4] = chksum + + elif addr == MSG_BrakeSysFeatures: + chksum = dat[0] + dat[1] # Veh_V_ActlBrk + chksum += (dat[2] >> 2) & 0xf # VehVActlBrk_No_Cnt + chksum += dat[2] >> 6 # VehVActlBrk_D_Qf + chksum = 0xff - (chksum & 0xff) + ret[3] = chksum + + elif addr == MSG_EngVehicleSpThrottle2: + chksum = (dat[2] >> 3) & 0xf # VehVActlEng_No_Cnt + chksum += (dat[4] >> 5) & 0x3 # VehVActlEng_D_Qf + chksum += dat[6] + dat[7] # Veh_V_ActlEng + chksum = 0xff - (chksum & 0xff) + ret[1] = chksum + + return addr, t, ret, bus + + +class Buttons: + CANCEL = 0 + RESUME = 1 + TJA_TOGGLE = 2 + + +# Ford safety has four different configurations tested here: +# * CAN with stock longitudinal +# * CAN with openpilot longitudinal +# * CAN FD with stock longitudinal +# * CAN FD with openpilot longitudinal + +class TestFordSafetyBase(common.PandaCarSafetyTest): + STANDSTILL_THRESHOLD = 1 + RELAY_MALFUNCTION_ADDRS = {0: (MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, + MSG_LateralMotionControl2, MSG_IPMA_Data)} + + FWD_BLACKLISTED_ADDRS = {2: [MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, + MSG_LateralMotionControl2, MSG_IPMA_Data]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + # Max allowed delta between car speeds + MAX_SPEED_DELTA = 2.0 # m/s + + STEER_MESSAGE = 0 + + # Curvature control limits + DEG_TO_CAN = 50000 # 1 / (2e-5) rad to can + MAX_CURVATURE = 0.02 + MAX_CURVATURE_ERROR = 0.002 + CURVATURE_ERROR_MIN_SPEED = 10.0 # m/s + + ANGLE_RATE_BP = [5., 25., 25.] + ANGLE_RATE_UP = [0.0002, 0.0001, 0.0001] # windup limit + ANGLE_RATE_DOWN = [0.000225, 0.00015, 0.00015] # unwind limit + + cnt_speed = 0 + cnt_speed_2 = 0 + cnt_yaw_rate = 0 + + packer: CANPackerPanda + safety: libpanda_py.Panda + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestFordSafetyBase": + raise unittest.SkipTest + + def _set_prev_desired_angle(self, t): + t = round(t * self.DEG_TO_CAN) + self.safety.set_desired_angle_last(t) + + def _reset_curvature_measurement(self, curvature, speed): + for _ in range(6): + self._rx(self._speed_msg(speed)) + self._rx(self._yaw_rate_msg(curvature, speed)) + + # Driver brake pedal + def _user_brake_msg(self, brake: bool): + # brake pedal and cruise state share same message, so we have to send + # the other signal too + enable = self.safety.get_controls_allowed() + values = { + "BpedDrvAppl_D_Actl": 2 if brake else 1, + "CcStat_D_Actl": 5 if enable else 0, + } + return self.packer.make_can_msg_panda("EngBrakeData", 0, values) + + # ABS vehicle speed + def _speed_msg(self, speed: float, quality_flag=True): + values = {"Veh_V_ActlBrk": speed * 3.6, "VehVActlBrk_D_Qf": 3 if quality_flag else 0, "VehVActlBrk_No_Cnt": self.cnt_speed % 16} + self.__class__.cnt_speed += 1 + return self.packer.make_can_msg_panda("BrakeSysFeatures", 0, values, fix_checksum=checksum) + + # PCM vehicle speed + def _speed_msg_2(self, speed: float, quality_flag=True): + values = {"Veh_V_ActlEng": speed * 3.6, "VehVActlEng_D_Qf": 3 if quality_flag else 0, "VehVActlEng_No_Cnt": self.cnt_speed_2 % 16} + self.__class__.cnt_speed_2 += 1 + return self.packer.make_can_msg_panda("EngVehicleSpThrottle2", 0, values, fix_checksum=checksum) + + # Standstill state + def _vehicle_moving_msg(self, speed: float): + values = {"VehStop_D_Stat": 1 if speed <= self.STANDSTILL_THRESHOLD else random.choice((0, 2, 3))} + return self.packer.make_can_msg_panda("DesiredTorqBrk", 0, values) + + # Current curvature + def _yaw_rate_msg(self, curvature: float, speed: float, quality_flag=True): + values = {"VehYaw_W_Actl": curvature * speed, "VehYawWActl_D_Qf": 3 if quality_flag else 0, + "VehRollYaw_No_Cnt": self.cnt_yaw_rate % 256} + self.__class__.cnt_yaw_rate += 1 + return self.packer.make_can_msg_panda("Yaw_Data_FD1", 0, values, fix_checksum=checksum) + + # Drive throttle input + def _user_gas_msg(self, gas: float): + values = {"ApedPos_Pc_ActlArb": gas} + return self.packer.make_can_msg_panda("EngVehicleSpThrottle", 0, values) + + # Cruise status + def _pcm_status_msg(self, enable: bool): + # brake pedal and cruise state share same message, so we have to send + # the other signal too + brake = self.safety.get_brake_pressed_prev() + values = { + "BpedDrvAppl_D_Actl": 2 if brake else 1, + "CcStat_D_Actl": 5 if enable else 0, + } + return self.packer.make_can_msg_panda("EngBrakeData", 0, values) + + # LKAS command + def _lkas_command_msg(self, action: int): + values = { + "LkaActvStats_D2_Req": action, + } + return self.packer.make_can_msg_panda("Lane_Assist_Data1", 0, values) + + # LCA command + def _lat_ctl_msg(self, enabled: bool, path_offset: float, path_angle: float, curvature: float, curvature_rate: float): + if self.STEER_MESSAGE == MSG_LateralMotionControl: + values = { + "LatCtl_D_Rq": 1 if enabled else 0, + "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter + "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians + "LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2 + "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter + } + return self.packer.make_can_msg_panda("LateralMotionControl", 0, values) + elif self.STEER_MESSAGE == MSG_LateralMotionControl2: + values = { + "LatCtl_D2_Rq": 1 if enabled else 0, + "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter + "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians + "LatCtlCrv_NoRate2_Actl": curvature_rate, # Curvature rate [-0.001024|0.001023] 1/meter^2 + "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter + } + return self.packer.make_can_msg_panda("LateralMotionControl2", 0, values) + + # Cruise control buttons + def _acc_button_msg(self, button: int, bus: int): + values = { + "CcAslButtnCnclPress": 1 if button == Buttons.CANCEL else 0, + "CcAsllButtnResPress": 1 if button == Buttons.RESUME else 0, + "TjaButtnOnOffPress": 1 if button == Buttons.TJA_TOGGLE else 0, + } + return self.packer.make_can_msg_panda("Steering_Data_FD1", bus, values) + + def test_rx_hook(self): + # checksum, counter, and quality flag checks + for quality_flag in [True, False]: + for msg in ["speed", "speed_2", "yaw"]: + self.safety.set_controls_allowed(True) + # send multiple times to verify counter checks + for _ in range(10): + if msg == "speed": + to_push = self._speed_msg(0, quality_flag=quality_flag) + elif msg == "speed_2": + to_push = self._speed_msg_2(0, quality_flag=quality_flag) + elif msg == "yaw": + to_push = self._yaw_rate_msg(0, 0, quality_flag=quality_flag) + + self.assertEqual(quality_flag, self._rx(to_push)) + self.assertEqual(quality_flag, self.safety.get_controls_allowed()) + + # Mess with checksum to make it fail, checksum is not checked for 2nd speed + to_push[0].data[3] = 0 # Speed checksum & half of yaw signal + should_rx = msg == "speed_2" and quality_flag + self.assertEqual(should_rx, self._rx(to_push)) + self.assertEqual(should_rx, self.safety.get_controls_allowed()) + + def test_rx_hook_speed_mismatch(self): + # Ford relies on speed for driver curvature limiting, so it checks two sources + for speed in np.arange(0, 40, 0.5): + for speed_delta in np.arange(-5, 5, 0.1): + speed_2 = round(max(speed + speed_delta, 0), 1) + # Set controls allowed in between rx since first message can reset it + self._rx(self._speed_msg(speed)) + self.safety.set_controls_allowed(True) + self._rx(self._speed_msg_2(speed_2)) + + within_delta = abs(speed - speed_2) <= self.MAX_SPEED_DELTA + self.assertEqual(self.safety.get_controls_allowed(), within_delta) + + def test_angle_measurements(self): + """Tests rx hook correctly parses the curvature measurement from the vehicle speed and yaw rate""" + for speed in np.arange(0.5, 40, 0.5): + for curvature in np.arange(0, self.MAX_CURVATURE * 2, 2e-3): + self._rx(self._speed_msg(speed)) + for c in (curvature, -curvature, 0, 0, 0, 0): + self._rx(self._yaw_rate_msg(c, speed)) + + self.assertEqual(self.safety.get_angle_meas_min(), round(-curvature * self.DEG_TO_CAN)) + self.assertEqual(self.safety.get_angle_meas_max(), round(curvature * self.DEG_TO_CAN)) + + self._rx(self._yaw_rate_msg(0, speed)) + self.assertEqual(self.safety.get_angle_meas_min(), round(-curvature * self.DEG_TO_CAN)) + self.assertEqual(self.safety.get_angle_meas_max(), 0) + + self._rx(self._yaw_rate_msg(0, speed)) + self.assertEqual(self.safety.get_angle_meas_min(), 0) + self.assertEqual(self.safety.get_angle_meas_max(), 0) + + def test_steer_allowed(self): + path_offsets = np.arange(-5.12, 5.11, 1).round() + path_angles = np.arange(-0.5, 0.5235, 0.1).round(1) + curvature_rates = np.arange(-0.001024, 0.00102375, 0.001).round(3) + curvatures = np.arange(-0.02, 0.02094, 0.01).round(2) + + for speed in (self.CURVATURE_ERROR_MIN_SPEED - 1, + self.CURVATURE_ERROR_MIN_SPEED + 1): + for controls_allowed in (True, False): + for steer_control_enabled in (True, False): + for path_offset in path_offsets: + for path_angle in path_angles: + for curvature_rate in curvature_rates: + for curvature in curvatures: + self.safety.set_controls_allowed(controls_allowed) + self._set_prev_desired_angle(curvature) + self._reset_curvature_measurement(curvature, speed) + + should_tx = path_offset == 0 and path_angle == 0 and curvature_rate == 0 + # when request bit is 0, only allow curvature of 0 since the signal range + # is not large enough to enforce it tracking measured + should_tx = should_tx and (controls_allowed if steer_control_enabled else curvature == 0) + with self.subTest(controls_allowed=controls_allowed, steer_control_enabled=steer_control_enabled, + path_offset=path_offset, path_angle=path_angle, curvature_rate=curvature_rate, + curvature=curvature): + self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(steer_control_enabled, path_offset, path_angle, curvature, curvature_rate))) + + def test_curvature_rate_limit_up(self): + """ + When the curvature error is exceeded, commanded curvature must start moving towards meas respecting rate limits. + Since panda allows higher rate limits to avoid false positives, we need to allow a lower rate to move towards meas. + """ + self.safety.set_controls_allowed(True) + small_curvature = 2 / self.DEG_TO_CAN # significant small amount of curvature to cross boundary + + for speed in np.arange(0, 40, 0.5): + limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED + max_delta_up = np.interp(speed, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) + max_delta_up_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) + + cases = [ + (not limit_command, 0), + (not limit_command, max_delta_up_lower - small_curvature), + (True, max_delta_up_lower), + (True, max_delta_up), + (False, max_delta_up + small_curvature), + ] + + for sign in (-1, 1): + self._reset_curvature_measurement(sign * (self.MAX_CURVATURE_ERROR + 1e-3), speed) + for should_tx, curvature in cases: + self._set_prev_desired_angle(sign * small_curvature) + self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * (small_curvature + curvature), 0))) + + def test_curvature_rate_limit_down(self): + self.safety.set_controls_allowed(True) + small_curvature = 2 / self.DEG_TO_CAN # significant small amount of curvature to cross boundary + + for speed in np.arange(0, 40, 0.5): + limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED + max_delta_down = np.interp(speed, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) + max_delta_down_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) + + cases = [ + (not limit_command, self.MAX_CURVATURE), + (not limit_command, self.MAX_CURVATURE - max_delta_down_lower + small_curvature), + (True, self.MAX_CURVATURE - max_delta_down_lower), + (True, self.MAX_CURVATURE - max_delta_down), + (False, self.MAX_CURVATURE - max_delta_down - small_curvature), + ] + + for sign in (-1, 1): + self._reset_curvature_measurement(sign * (self.MAX_CURVATURE - self.MAX_CURVATURE_ERROR - 1e-3), speed) + for should_tx, curvature in cases: + self._set_prev_desired_angle(sign * self.MAX_CURVATURE) + self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * curvature, 0))) + + def test_prevent_lkas_action(self): + self.safety.set_controls_allowed(1) + self.assertFalse(self._tx(self._lkas_command_msg(1))) + + self.safety.set_controls_allowed(0) + self.assertFalse(self._tx(self._lkas_command_msg(1))) + + def test_acc_buttons(self): + for allowed in (0, 1): + self.safety.set_controls_allowed(allowed) + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertTrue(self._tx(self._acc_button_msg(Buttons.TJA_TOGGLE, 2))) + + for allowed in (0, 1): + self.safety.set_controls_allowed(allowed) + for bus in (0, 2): + self.assertEqual(allowed, self._tx(self._acc_button_msg(Buttons.RESUME, bus))) + + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + for bus in (0, 2): + self.assertEqual(enabled, self._tx(self._acc_button_msg(Buttons.CANCEL, bus))) + + +class TestFordStockSafety(TestFordSafetyBase): + STEER_MESSAGE = MSG_LateralMotionControl + + TX_MSGS = [ + [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], + [MSG_LateralMotionControl, 0], [MSG_IPMA_Data, 0], + ] + + def setUp(self): + self.packer = CANPackerPanda("ford_lincoln_base_pt") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_FORD, 0) + self.safety.init_tests() + + +class TestFordCANFDStockSafety(TestFordSafetyBase): + STEER_MESSAGE = MSG_LateralMotionControl2 + + TX_MSGS = [ + [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], + [MSG_LateralMotionControl2, 0], [MSG_IPMA_Data, 0], + ] + + def setUp(self): + self.packer = CANPackerPanda("ford_lincoln_base_pt") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.FLAG_FORD_CANFD) + self.safety.init_tests() + + +class TestFordLongitudinalSafetyBase(TestFordSafetyBase): + RELAY_MALFUNCTION_ADDRS = {0: (MSG_ACCDATA, MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, + MSG_LateralMotionControl2, MSG_IPMA_Data)} + + FWD_BLACKLISTED_ADDRS = {2: [MSG_ACCDATA, MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, + MSG_LateralMotionControl2, MSG_IPMA_Data]} + + MAX_ACCEL = 2.0 # accel is used for brakes, but openpilot can set positive values + MIN_ACCEL = -3.5 + INACTIVE_ACCEL = 0.0 + + MAX_GAS = 2.0 + MIN_GAS = -0.5 + INACTIVE_GAS = -5.0 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestFordLongitudinalSafetyBase": + raise unittest.SkipTest + + # ACC command + def _acc_command_msg(self, gas: float, brake: float, cmbb_deny: bool = False): + values = { + "AccPrpl_A_Rq": gas, # [-5|5.23] m/s^2 + "AccPrpl_A_Pred": gas, # [-5|5.23] m/s^2 + "AccBrkTot_A_Rq": brake, # [-20|11.9449] m/s^2 + "CmbbDeny_B_Actl": 1 if cmbb_deny else 0, # [0|1] deny AEB actuation + } + return self.packer.make_can_msg_panda("ACCDATA", 0, values) + + def test_stock_aeb(self): + # Test that CmbbDeny_B_Actl is never 1, it prevents the ABS module from actuating AEB requests from ACCDATA_2 + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + for cmbb_deny in (True, False): + should_tx = not cmbb_deny + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, self.INACTIVE_ACCEL, cmbb_deny))) + should_tx = controls_allowed and not cmbb_deny + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.MAX_GAS, self.MAX_ACCEL, cmbb_deny))) + + def test_gas_safety_check(self): + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + for gas in np.concatenate((np.arange(self.MIN_GAS - 2, self.MAX_GAS + 2, 0.05), [self.INACTIVE_GAS])): + gas = round(gas, 2) # floats might not hit exact boundary conditions without rounding + should_tx = (controls_allowed and self.MIN_GAS <= gas <= self.MAX_GAS) or gas == self.INACTIVE_GAS + self.assertEqual(should_tx, self._tx(self._acc_command_msg(gas, self.INACTIVE_ACCEL))) + + def test_brake_safety_check(self): + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + for brake in np.arange(self.MIN_ACCEL - 2, self.MAX_ACCEL + 2, 0.05): + brake = round(brake, 2) # floats might not hit exact boundary conditions without rounding + should_tx = (controls_allowed and self.MIN_ACCEL <= brake <= self.MAX_ACCEL) or brake == self.INACTIVE_ACCEL + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, brake))) + + +class TestFordLongitudinalSafety(TestFordLongitudinalSafetyBase): + STEER_MESSAGE = MSG_LateralMotionControl + + TX_MSGS = [ + [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA, 0], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], + [MSG_LateralMotionControl, 0], [MSG_IPMA_Data, 0], + ] + + def setUp(self): + self.packer = CANPackerPanda("ford_lincoln_base_pt") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.FLAG_FORD_LONG_CONTROL) + self.safety.init_tests() + + +class TestFordCANFDLongitudinalSafety(TestFordLongitudinalSafetyBase): + STEER_MESSAGE = MSG_LateralMotionControl2 + + TX_MSGS = [ + [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA, 0], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], + [MSG_LateralMotionControl2, 0], [MSG_IPMA_Data, 0], + ] + + def setUp(self): + self.packer = CANPackerPanda("ford_lincoln_base_pt") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.FLAG_FORD_LONG_CONTROL | Panda.FLAG_FORD_CANFD) + self.safety.init_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_gm.py b/panda/tests/safety/test_gm.py new file mode 100644 index 0000000..6b02991 --- /dev/null +++ b/panda/tests/safety/test_gm.py @@ -0,0 +1,383 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + + +class Buttons: + UNPRESS = 1 + RES_ACCEL = 2 + DECEL_SET = 3 + CANCEL = 6 + + +class GmLongitudinalBase(common.PandaCarSafetyTest, common.LongitudinalGasBrakeSafetyTest): + # pylint: disable=no-member,abstract-method + + RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB)} # ASCMLKASteeringCmd, ASCMGasRegenCmd + + MAX_POSSIBLE_BRAKE = 2 ** 12 + MAX_BRAKE = 400 + + MAX_POSSIBLE_GAS = 2 ** 12 + + PCM_CRUISE = False # openpilot can control the PCM state if longitudinal + + def _send_brake_msg(self, brake): + values = {"FrictionBrakeCmd": -brake} + return self.packer_chassis.make_can_msg_panda("EBCMFrictionBrakeCmd", self.BRAKE_BUS, values) + + def _send_gas_msg(self, gas): + values = {"GasRegenCmd": gas} + return self.packer.make_can_msg_panda("ASCMGasRegenCmd", 0, values) + + # override these tests from PandaCarSafetyTest, GM longitudinal uses button enable + def _pcm_status_msg(self, enable): + raise NotImplementedError + + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def test_set_resume_buttons(self): + """ + SET and RESUME enter controls allowed on their falling and rising edges, respectively. + """ + for btn_prev in range(8): + for btn_cur in range(8): + with self.subTest(btn_prev=btn_prev, btn_cur=btn_cur): + self._rx(self._button_msg(btn_prev)) + self.safety.set_controls_allowed(0) + for _ in range(10): + self._rx(self._button_msg(btn_cur)) + + should_enable = btn_cur != Buttons.DECEL_SET and btn_prev == Buttons.DECEL_SET + should_enable = should_enable or (btn_cur == Buttons.RES_ACCEL and btn_prev != Buttons.RES_ACCEL) + should_enable = should_enable and btn_cur != Buttons.CANCEL + self.assertEqual(should_enable, self.safety.get_controls_allowed()) + + def test_cancel_button(self): + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(Buttons.CANCEL)) + self.assertFalse(self.safety.get_controls_allowed()) + + +class TestGmSafetyBase(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): + STANDSTILL_THRESHOLD = 10 * 0.0311 + # Ensures ASCM is off on ASCM cars, and relay is not malfunctioning for camera-ACC cars + RELAY_MALFUNCTION_ADDRS = {0: (0x180,)} # ASCMLKASteeringCmd + BUTTONS_BUS = 0 # rx or tx + BRAKE_BUS = 0 # tx only + + MAX_RATE_UP = 10 + MAX_RATE_DOWN = 15 + MAX_TORQUE = 300 + MAX_RT_DELTA = 128 + RT_INTERVAL = 250000 + DRIVER_TORQUE_ALLOWANCE = 65 + DRIVER_TORQUE_FACTOR = 4 + + PCM_CRUISE = True # openpilot is tied to the PCM state if not longitudinal + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestGmSafetyBase": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, 0) + self.safety.init_tests() + + def _pcm_status_msg(self, enable): + if self.PCM_CRUISE: + values = {"CruiseState": enable} + return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) + else: + raise NotImplementedError + + def _speed_msg(self, speed): + values = {"%sWheelSpd" % s: speed for s in ["RL", "RR"]} + return self.packer.make_can_msg_panda("EBCMWheelSpdRear", 0, values) + + def _user_brake_msg(self, brake): + # GM safety has a brake threshold of 8 + values = {"BrakePedalPos": 8 if brake else 0} + return self.packer.make_can_msg_panda("ECMAcceleratorPos", 0, values) + + def _user_regen_msg(self, regen): + values = {"RegenPaddle": 2 if regen else 0} + return self.packer.make_can_msg_panda("EBCMRegenPaddle", 0, values) + + def _user_gas_msg(self, gas): + values = {"AcceleratorPedal2": 1 if gas else 0} + if self.PCM_CRUISE: + # Fill CruiseState with expected value if the safety mode reads cruise state from gas msg + values["CruiseState"] = self.safety.get_controls_allowed() + return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) + + def _torque_driver_msg(self, torque): + # Safety tests assume driver torque is an int, use DBC factor + values = {"LKADriverAppldTrq": torque * 0.01} + return self.packer.make_can_msg_panda("PSCMStatus", 0, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"LKASteeringCmd": torque, "LKASteeringCmdActive": steer_req} + return self.packer.make_can_msg_panda("ASCMLKASteeringCmd", 0, values) + + def _button_msg(self, buttons): + values = {"ACCButtons": buttons} + return self.packer.make_can_msg_panda("ASCMSteeringButton", self.BUTTONS_BUS, values) + + +class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase): + TX_MSGS = [[0x180, 0], [0x409, 0], [0x40A, 0], [0x2CB, 0], [0x370, 0], # pt bus + [0xA1, 1], [0x306, 1], [0x308, 1], [0x310, 1], # obs bus + [0x315, 2], # ch bus + [0x104c006c, 3], [0x10400060, 3]] # gmlan + FWD_BLACKLISTED_ADDRS: dict[int, list[int]] = {} + FWD_BUS_LOOKUP: dict[int, int] = {} + BRAKE_BUS = 2 + + MAX_GAS = 3072 + MIN_GAS = 1404 # maximum regen + INACTIVE_GAS = 1404 + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, 0) + self.safety.init_tests() + + +class TestGmCameraSafetyBase(TestGmSafetyBase): + + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestGmCameraSafetyBase": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _user_brake_msg(self, brake): + values = {"BrakePressed": brake} + return self.packer.make_can_msg_panda("ECMEngineStatus", 0, values) + + +class TestGmCameraSafety(TestGmCameraSafetyBase): + TX_MSGS = [[0x180, 0], # pt bus + [0x184, 2]] # camera bus + FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184]} # block LKAS message and PSCMStatus + BUTTONS_BUS = 2 # tx only + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_CAM) + self.safety.init_tests() + + def test_buttons(self): + # Only CANCEL button is allowed while cruise is enabled + self.safety.set_controls_allowed(0) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) + + +class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase): + TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x370, 0], # pt bus + [0x184, 2]] # camera bus + FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus + BUTTONS_BUS = 0 # rx only + + MAX_GAS = 3400 + MIN_GAS = 1514 # maximum regen + INACTIVE_GAS = 1554 + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_CAM | Panda.FLAG_GM_HW_CAM_LONG) + self.safety.init_tests() + +class TestGmSdgmSafety(TestGmSafetyBase): + FWD_BUS_LOOKUP = {0: 2, 2: 0} + TX_MSGS = [[0x180, 0], [0x1E1, 0], # pt bus + [0x184, 2]] # obj bus + FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184]} # block LKAS message and PSCMStatus + BUTTONS_BUS = 0 # tx + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_SDGM) + self.safety.init_tests() + + def _user_brake_msg(self, brake): + values = {"BrakePressed": brake} + return self.packer.make_can_msg_panda("ECMEngineStatus", 0, values) + + def test_buttons(self): + # Only CANCEL button is allowed while cruise is enabled + self.safety.set_controls_allowed(0) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) + +##### OPGM TESTS ##### + +def interceptor_msg(gas, addr): + to_send = common.make_msg(0, addr, 6) + to_send[0].data[0] = (gas & 0xFF00) >> 8 + to_send[0].data[1] = gas & 0xFF + to_send[0].data[2] = (gas & 0xFF00) >> 8 + to_send[0].data[3] = gas & 0xFF + return to_send + + +class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafety): + INTERCEPTOR_THRESHOLD = 515 + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks( + Panda.SAFETY_GM, + Panda.FLAG_GM_HW_CAM | Panda.FLAG_GM_NO_ACC | Panda.FLAG_GM_PEDAL_LONG | Panda.FLAG_GM_GAS_INTERCEPTOR) + self.safety.init_tests() + + def test_pcm_sets_cruise_engaged(self): + for enabled in [True, False]: + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self.safety.get_cruise_engaged_prev()) + + def test_no_pcm_enable(self): + self._rx(self._interceptor_user_gas(0)) + self.safety.set_controls_allowed(0) + self.assertFalse(self.safety.get_controls_allowed()) + self._rx(self._pcm_status_msg(True)) + self.assertFalse(self.safety.get_controls_allowed()) + self.assertTrue(self.safety.get_cruise_engaged_prev()) + + def test_no_response_to_acc_pcm_message(self): + self._rx(self._interceptor_user_gas(0)) + def _acc_pcm_msg(enable): + values = {"CruiseState": enable} + return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) + for enable in [True, False]: + self.safety.set_controls_allowed(enable) + self._rx(_acc_pcm_msg(True)) + self.assertEqual(enable, self.safety.get_controls_allowed()) + self._rx(_acc_pcm_msg(False)) + self.assertEqual(enable, self.safety.get_controls_allowed()) + + def test_buttons(self): + self._rx(self._interceptor_user_gas(0)) + # Only CANCEL button is allowed while cruise is enabled + self.safety.set_controls_allowed(0) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for enabled in (True, False): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_fwd_hook(self): + pass + + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def _interceptor_gas_cmd(self, gas): + return interceptor_msg(gas, 0x200) + + def _interceptor_user_gas(self, gas): + return interceptor_msg(gas, 0x201) + + def _pcm_status_msg(self, enable): + values = {"CruiseActive": enable} + return self.packer.make_can_msg_panda("ECMCruiseControl", 0, values) + + +class TestGmCcLongitudinalSafety(TestGmCameraSafety): + TX_MSGS = [[384, 0], [481, 0], [388, 2]] + FWD_BLACKLISTED_ADDRS = {2: [384], 0: [388]} # block LKAS message and PSCMStatus + BUTTONS_BUS = 0 # tx only + + MAX_GAS = 3400 + MAX_REGEN = 1514 + INACTIVE_REGEN = 1554 + MAX_BRAKE = 400 + + def setUp(self): + self.packer = CANPackerPanda("gm_global_a_powertrain_generated") + self.packer_chassis = CANPackerPanda("gm_global_a_chassis") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_GM, Panda.FLAG_GM_HW_CAM | Panda.FLAG_GM_NO_ACC | Panda.FLAG_GM_CC_LONG) + self.safety.init_tests() + + def _pcm_status_msg(self, enable): + values = {"CruiseActive": enable} + return self.packer.make_can_msg_panda("ECMCruiseControl", 0, values) + + def test_fwd_hook(self): + pass + + def test_buttons(self): + self.safety.set_controls_allowed(0) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + self.safety.set_controls_allowed(1) + for btn in range(8): + self.assertFalse(self._tx(self._button_msg(btn))) + + for enabled in (True, False): + for btn in (Buttons.RES_ACCEL, Buttons.DECEL_SET, Buttons.CANCEL): + self._rx(self._pcm_status_msg(enabled)) + self.assertEqual(enabled, self._tx(self._button_msg(btn))) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_honda.py b/panda/tests/safety/test_honda.py new file mode 100644 index 0000000..45f190c --- /dev/null +++ b/panda/tests/safety/test_honda.py @@ -0,0 +1,618 @@ +#!/usr/bin/env python3 +import unittest +import numpy as np + +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda, MAX_WRONG_COUNTERS + +HONDA_N_COMMON_TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x30C, 0], [0x33D, 0]] + +class Btn: + NONE = 0 + MAIN = 1 + CANCEL = 2 + SET = 3 + RESUME = 4 + +HONDA_NIDEC = 0 +HONDA_BOSCH = 1 + + +# Honda safety has several different configurations tested here: +# * Nidec +# * normal (PCM-enable) +# * alt SCM messages (PCM-enable) +# * gas interceptor (button-enable) +# * gas interceptor with alt SCM messages (button-enable) +# * Bosch +# * Bosch with Longitudinal Support +# * Bosch Radarless +# * Bosch Radarless with Longitudinal Support + + +class HondaButtonEnableBase(common.PandaCarSafetyTest): + # pylint: disable=no-member,abstract-method + + # override these inherited tests since we're using button enable + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def test_buttons_with_main_off(self): + for btn in (Btn.SET, Btn.RESUME, Btn.CANCEL): + self.safety.set_controls_allowed(1) + self._rx(self._acc_state_msg(False)) + self._rx(self._button_msg(btn, main_on=False)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_set_resume_buttons(self): + """ + Both SET and RES should enter controls allowed on their falling edge. + """ + for main_on in (True, False): + self._rx(self._acc_state_msg(main_on)) + for btn_prev in range(8): + for btn_cur in range(8): + self._rx(self._button_msg(Btn.NONE)) + self.safety.set_controls_allowed(0) + for _ in range(10): + self._rx(self._button_msg(btn_prev)) + self.assertFalse(self.safety.get_controls_allowed()) + + # should enter controls allowed on falling edge and not transitioning to cancel or main + should_enable = (main_on and + btn_cur != btn_prev and + btn_prev in (Btn.RESUME, Btn.SET) and + btn_cur not in (Btn.CANCEL, Btn.MAIN)) + + self._rx(self._button_msg(btn_cur, main_on=main_on)) + self.assertEqual(should_enable, self.safety.get_controls_allowed(), msg=f"{main_on=} {btn_prev=} {btn_cur=}") + + def test_main_cancel_buttons(self): + """ + Both MAIN and CANCEL should exit controls immediately. + """ + for btn in (Btn.MAIN, Btn.CANCEL): + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(btn, main_on=True)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_disengage_on_main(self): + self.safety.set_controls_allowed(1) + self._rx(self._acc_state_msg(True)) + self.assertTrue(self.safety.get_controls_allowed()) + self._rx(self._acc_state_msg(False)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_rx_hook(self): + + # TODO: move this test to common + # checksum checks + for msg in ["btn", "gas", "speed"]: + self.safety.set_controls_allowed(1) + if msg == "btn": + to_push = self._button_msg(Btn.SET) + if msg == "gas": + to_push = self._user_gas_msg(0) + if msg == "speed": + to_push = self._speed_msg(0) + self.assertTrue(self._rx(to_push)) + if msg != "btn": + to_push[0].data[4] = 0 # invalidate checksum + to_push[0].data[5] = 0 + to_push[0].data[6] = 0 + to_push[0].data[7] = 0 + self.assertFalse(self._rx(to_push)) + self.assertFalse(self.safety.get_controls_allowed()) + + # counter + # reset wrong_counters to zero by sending valid messages + for i in range(MAX_WRONG_COUNTERS + 1): + self.__class__.cnt_speed += 1 + self.__class__.cnt_button += 1 + self.__class__.cnt_powertrain_data += 1 + if i < MAX_WRONG_COUNTERS: + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(Btn.SET)) + self._rx(self._speed_msg(0)) + self._rx(self._user_gas_msg(0)) + else: + self.assertFalse(self._rx(self._button_msg(Btn.SET))) + self.assertFalse(self._rx(self._speed_msg(0))) + self.assertFalse(self._rx(self._user_gas_msg(0))) + self.assertFalse(self.safety.get_controls_allowed()) + + # restore counters for future tests with a couple of good messages + for _ in range(2): + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(Btn.SET, main_on=True)) + self._rx(self._speed_msg(0)) + self._rx(self._user_gas_msg(0)) + self._rx(self._button_msg(Btn.SET, main_on=True)) + self.assertTrue(self.safety.get_controls_allowed()) + + +class HondaPcmEnableBase(common.PandaCarSafetyTest): + # pylint: disable=no-member,abstract-method + + def test_buttons(self): + """ + Buttons should only cancel in this configuration, + since our state is tied to the PCM's cruise state. + """ + for controls_allowed in (True, False): + for main_on in (True, False): + # not a valid state + if controls_allowed and not main_on: + continue + + for btn in (Btn.SET, Btn.RESUME, Btn.CANCEL): + self.safety.set_controls_allowed(controls_allowed) + self._rx(self._acc_state_msg(main_on)) + + # btn + none for falling edge + self._rx(self._button_msg(btn, main_on=main_on)) + self._rx(self._button_msg(Btn.NONE, main_on=main_on)) + + if btn == Btn.CANCEL: + self.assertFalse(self.safety.get_controls_allowed()) + else: + self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) + + +class HondaBase(common.PandaCarSafetyTest): + MAX_BRAKE = 255 + PT_BUS: int | None = None # must be set when inherited + STEER_BUS: int | None = None # must be set when inherited + BUTTONS_BUS: int | None = None # must be set when inherited, tx on this bus, rx on PT_BUS + + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x194)} # STEERING_CONTROL + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + cnt_speed = 0 + cnt_button = 0 + cnt_brake = 0 + cnt_powertrain_data = 0 + cnt_acc_state = 0 + + @classmethod + def setUpClass(cls): + if cls.__name__.endswith("Base"): + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _powertrain_data_msg(self, cruise_on=None, brake_pressed=None, gas_pressed=None): + # preserve the state + if cruise_on is None: + # or'd with controls allowed since the tests use it to "enable" cruise + cruise_on = self.safety.get_cruise_engaged_prev() or self.safety.get_controls_allowed() + if brake_pressed is None: + brake_pressed = self.safety.get_brake_pressed_prev() + if gas_pressed is None: + gas_pressed = self.safety.get_gas_pressed_prev() + + values = { + "ACC_STATUS": cruise_on, + "BRAKE_PRESSED": brake_pressed, + "PEDAL_GAS": gas_pressed, + "COUNTER": self.cnt_powertrain_data % 4 + } + self.__class__.cnt_powertrain_data += 1 + return self.packer.make_can_msg_panda("POWERTRAIN_DATA", self.PT_BUS, values) + + def _pcm_status_msg(self, enable): + return self._powertrain_data_msg(cruise_on=enable) + + def _speed_msg(self, speed): + values = {"XMISSION_SPEED": speed, "COUNTER": self.cnt_speed % 4} + self.__class__.cnt_speed += 1 + return self.packer.make_can_msg_panda("ENGINE_DATA", self.PT_BUS, values) + + def _acc_state_msg(self, main_on): + values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} + self.__class__.cnt_acc_state += 1 + return self.packer.make_can_msg_panda("SCM_FEEDBACK", self.PT_BUS, values) + + def _button_msg(self, buttons, main_on=False, bus=None): + bus = self.PT_BUS if bus is None else bus + values = {"CRUISE_BUTTONS": buttons, "COUNTER": self.cnt_button % 4} + self.__class__.cnt_button += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", bus, values) + + def _user_brake_msg(self, brake): + return self._powertrain_data_msg(brake_pressed=brake) + + def _user_gas_msg(self, gas): + return self._powertrain_data_msg(gas_pressed=gas) + + def _send_steer_msg(self, steer): + values = {"STEER_TORQUE": steer} + return self.packer.make_can_msg_panda("STEERING_CONTROL", self.STEER_BUS, values) + + def _send_brake_msg(self, brake): + # must be implemented when inherited + raise NotImplementedError + + def test_disengage_on_brake(self): + self.safety.set_controls_allowed(1) + self._rx(self._user_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + def test_steer_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._send_steer_msg(0x0000))) + self.assertFalse(self._tx(self._send_steer_msg(0x1000))) + + +# ********************* Honda Nidec ********************** + + +class TestHondaNidecSafetyBase(HondaBase): + TX_MSGS = HONDA_N_COMMON_TX_MSGS + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} + + PT_BUS = 0 + STEER_BUS = 0 + BUTTONS_BUS = 0 + + MAX_GAS = 198 + + def setUp(self): + self.packer = CANPackerPanda("honda_civic_touring_2016_can_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, 0) + self.safety.init_tests() + + def _send_brake_msg(self, brake, aeb_req=0, bus=0): + values = {"COMPUTER_BRAKE": brake, "AEB_REQ_1": aeb_req} + return self.packer.make_can_msg_panda("BRAKE_COMMAND", bus, values) + + def _rx_brake_msg(self, brake, aeb_req=0): + return self._send_brake_msg(brake, aeb_req, bus=2) + + def _send_acc_hud_msg(self, pcm_gas, pcm_speed): + # Used to control ACC on Nidec without pedal + values = {"PCM_GAS": pcm_gas, "PCM_SPEED": pcm_speed} + return self.packer.make_can_msg_panda("ACC_HUD", 0, values) + + def test_acc_hud_safety_check(self): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + for pcm_gas in range(255): + for pcm_speed in range(100): + send = (controls_allowed and pcm_gas <= self.MAX_GAS) or (pcm_gas == 0 and pcm_speed == 0) + self.assertEqual(send, self._tx(self._send_acc_hud_msg(pcm_gas, pcm_speed))) + + def test_fwd_hook(self): + # normal operation, not forwarding AEB + self.FWD_BLACKLISTED_ADDRS[2].append(0x1FA) + self.safety.set_honda_fwd_brake(False) + super().test_fwd_hook() + + # forwarding AEB brake signal + self.FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} + self.safety.set_honda_fwd_brake(True) + super().test_fwd_hook() + + def test_honda_fwd_brake_latching(self): + # Shouldn't fwd stock Honda requesting brake without AEB + self.assertTrue(self._rx(self._rx_brake_msg(self.MAX_BRAKE, aeb_req=0))) + self.assertFalse(self.safety.get_honda_fwd_brake()) + + # Now allow controls and request some brake + openpilot_brake = round(self.MAX_BRAKE / 2.0) + self.safety.set_controls_allowed(True) + self.assertTrue(self._tx(self._send_brake_msg(openpilot_brake))) + + # Still shouldn't fwd stock Honda brake until it's more than openpilot's + for stock_honda_brake in range(self.MAX_BRAKE + 1): + self.assertTrue(self._rx(self._rx_brake_msg(stock_honda_brake, aeb_req=1))) + should_fwd_brake = stock_honda_brake >= openpilot_brake + self.assertEqual(should_fwd_brake, self.safety.get_honda_fwd_brake()) + + # Shouldn't stop fwding until AEB event is over + for stock_honda_brake in range(self.MAX_BRAKE + 1)[::-1]: + self.assertTrue(self._rx(self._rx_brake_msg(stock_honda_brake, aeb_req=1))) + self.assertTrue(self.safety.get_honda_fwd_brake()) + + self.assertTrue(self._rx(self._rx_brake_msg(0, aeb_req=0))) + self.assertFalse(self.safety.get_honda_fwd_brake()) + + def test_brake_safety_check(self): + for fwd_brake in [False, True]: + self.safety.set_honda_fwd_brake(fwd_brake) + for brake in np.arange(0, self.MAX_BRAKE + 10, 1): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + if fwd_brake: + send = False # block openpilot brake msg when fwd'ing stock msg + elif controls_allowed: + send = self.MAX_BRAKE >= brake >= 0 + else: + send = brake == 0 + self.assertEqual(send, self._tx(self._send_brake_msg(brake))) + + +class TestHondaNidecPcmSafety(HondaPcmEnableBase, TestHondaNidecSafetyBase): + """ + Covers the Honda Nidec safety mode + """ + + # Nidec doesn't disengage on falling edge of cruise. See comment in safety_honda.h + def test_disable_control_allowed_from_cruise(self): + pass + + +class TestHondaNidecGasInterceptorSafety(common.GasInterceptorSafetyTest, HondaButtonEnableBase, TestHondaNidecSafetyBase): + """ + Covers the Honda Nidec safety mode with a gas interceptor, switches to a button-enable car + """ + + TX_MSGS = HONDA_N_COMMON_TX_MSGS + [[0x200, 0]] + INTERCEPTOR_THRESHOLD = 492 + + def setUp(self): + self.packer = CANPackerPanda("honda_civic_touring_2016_can_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, Panda.FLAG_HONDA_GAS_INTERCEPTOR) + self.safety.init_tests() + + +class TestHondaNidecPcmAltSafety(TestHondaNidecPcmSafety): + """ + Covers the Honda Nidec safety mode with alt SCM messages + """ + def setUp(self): + self.packer = CANPackerPanda("acura_ilx_2016_can_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, Panda.FLAG_HONDA_NIDEC_ALT) + self.safety.init_tests() + + def _acc_state_msg(self, main_on): + values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} + self.__class__.cnt_acc_state += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) + + def _button_msg(self, buttons, main_on=False, bus=None): + bus = self.PT_BUS if bus is None else bus + values = {"CRUISE_BUTTONS": buttons, "MAIN_ON": main_on, "COUNTER": self.cnt_button % 4} + self.__class__.cnt_button += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", bus, values) + + +class TestHondaNidecAltGasInterceptorSafety(common.GasInterceptorSafetyTest, HondaButtonEnableBase, TestHondaNidecSafetyBase): + """ + Covers the Honda Nidec safety mode with alt SCM messages and gas interceptor, switches to a button-enable car + """ + + TX_MSGS = HONDA_N_COMMON_TX_MSGS + [[0x200, 0]] + INTERCEPTOR_THRESHOLD = 492 + + def setUp(self): + self.packer = CANPackerPanda("acura_ilx_2016_can_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, Panda.FLAG_HONDA_NIDEC_ALT | Panda.FLAG_HONDA_GAS_INTERCEPTOR) + self.safety.init_tests() + + def _acc_state_msg(self, main_on): + values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} + self.__class__.cnt_acc_state += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) + + def _button_msg(self, buttons, main_on=False, bus=None): + bus = self.PT_BUS if bus is None else bus + values = {"CRUISE_BUTTONS": buttons, "MAIN_ON": main_on, "COUNTER": self.cnt_button % 4} + self.__class__.cnt_button += 1 + return self.packer.make_can_msg_panda("SCM_BUTTONS", bus, values) + + + +# ********************* Honda Bosch ********************** + + +class TestHondaBoschSafetyBase(HondaBase): + PT_BUS = 1 + STEER_BUS = 0 + BUTTONS_BUS = 1 + + TX_MSGS = [[0xE4, 0], [0xE5, 0], [0x296, 1], [0x33D, 0], [0x33DA, 0], [0x33DB, 0]] + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} + + def setUp(self): + self.packer = CANPackerPanda("honda_accord_2018_can_generated") + self.safety = libpanda_py.libpanda + + def _alt_brake_msg(self, brake): + values = {"BRAKE_PRESSED": brake, "COUNTER": self.cnt_brake % 4} + self.__class__.cnt_brake += 1 + return self.packer.make_can_msg_panda("BRAKE_MODULE", self.PT_BUS, values) + + def _send_brake_msg(self, brake): + pass + + def test_alt_disengage_on_brake(self): + self.safety.set_honda_alt_brake_msg(1) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertFalse(self.safety.get_controls_allowed()) + + self.safety.set_honda_alt_brake_msg(0) + self.safety.set_controls_allowed(1) + self._rx(self._alt_brake_msg(1)) + self.assertTrue(self.safety.get_controls_allowed()) + + def test_spam_cancel_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._button_msg(Btn.CANCEL, bus=self.BUTTONS_BUS))) + self.assertFalse(self._tx(self._button_msg(Btn.RESUME, bus=self.BUTTONS_BUS))) + self.assertFalse(self._tx(self._button_msg(Btn.SET, bus=self.BUTTONS_BUS))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._button_msg(Btn.RESUME, bus=self.BUTTONS_BUS))) + + +class TestHondaBoschAltBrakeSafetyBase(TestHondaBoschSafetyBase): + """ + Base Bosch safety test class with an alternate brake message + """ + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, Panda.FLAG_HONDA_ALT_BRAKE) + self.safety.init_tests() + + def _user_brake_msg(self, brake): + return self._alt_brake_msg(brake) + + def test_alt_brake_rx_hook(self): + self.safety.set_honda_alt_brake_msg(1) + self.safety.set_controls_allowed(1) + to_push = self._alt_brake_msg(0) + self.assertTrue(self._rx(to_push)) + to_push[0].data[2] = to_push[0].data[2] & 0xF0 # invalidate checksum + self.assertFalse(self._rx(to_push)) + self.assertFalse(self.safety.get_controls_allowed()) + + +class TestHondaBoschSafety(HondaPcmEnableBase, TestHondaBoschSafetyBase): + """ + Covers the Honda Bosch safety mode with stock longitudinal + """ + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, 0) + self.safety.init_tests() + + +class TestHondaBoschAltBrakeSafety(HondaPcmEnableBase, TestHondaBoschAltBrakeSafetyBase): + """ + Covers the Honda Bosch safety mode with stock longitudinal and an alternate brake message + """ + + +class TestHondaBoschLongSafety(HondaButtonEnableBase, TestHondaBoschSafetyBase): + """ + Covers the Honda Bosch safety mode with longitudinal control + """ + NO_GAS = -30000 + MAX_GAS = 2000 + MAX_ACCEL = 2.0 # accel is used for brakes, but openpilot can set positive values + MIN_ACCEL = -3.5 + + STEER_BUS = 1 + TX_MSGS = [[0xE4, 1], [0x1DF, 1], [0x1EF, 1], [0x1FA, 1], [0x30C, 1], [0x33D, 1], [0x33DA, 1], [0x33DB, 1], [0x39F, 1], [0x18DAB0F1, 1]] + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} + # 0x1DF is to test that radar is disabled + RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x194), 1: (0x1DF,)} # STEERING_CONTROL, ACC_CONTROL + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, Panda.FLAG_HONDA_BOSCH_LONG) + self.safety.init_tests() + + def _send_gas_brake_msg(self, gas, accel): + values = { + "GAS_COMMAND": gas, + "ACCEL_COMMAND": accel, + "BRAKE_REQUEST": accel < 0, + } + return self.packer.make_can_msg_panda("ACC_CONTROL", self.PT_BUS, values) + + # Longitudinal doesn't need to send buttons + def test_spam_cancel_safety_check(self): + pass + + def test_diagnostics(self): + tester_present = libpanda_py.make_CANPacket(0x18DAB0F1, self.PT_BUS, b"\x02\x3E\x80\x00\x00\x00\x00\x00") + self.assertTrue(self._tx(tester_present)) + + not_tester_present = libpanda_py.make_CANPacket(0x18DAB0F1, self.PT_BUS, b"\x03\xAA\xAA\x00\x00\x00\x00\x00") + self.assertFalse(self._tx(not_tester_present)) + + def test_gas_safety_check(self): + for controls_allowed in [True, False]: + for gas in np.arange(self.NO_GAS, self.MAX_GAS + 2000, 100): + accel = 0 if gas < 0 else gas / 1000 + self.safety.set_controls_allowed(controls_allowed) + send = (controls_allowed and 0 <= gas <= self.MAX_GAS) or gas == self.NO_GAS + self.assertEqual(send, self._tx(self._send_gas_brake_msg(gas, accel)), (controls_allowed, gas, accel)) + + def test_brake_safety_check(self): + for controls_allowed in [True, False]: + for accel in np.arange(self.MIN_ACCEL - 1, self.MAX_ACCEL + 1, 0.01): + accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding + self.safety.set_controls_allowed(controls_allowed) + send = self.MIN_ACCEL <= accel <= self.MAX_ACCEL if controls_allowed else accel == 0 + self.assertEqual(send, self._tx(self._send_gas_brake_msg(self.NO_GAS, accel)), (controls_allowed, accel)) + + +class TestHondaBoschRadarlessSafetyBase(TestHondaBoschSafetyBase): + """Base class for radarless Honda Bosch""" + PT_BUS = 0 + STEER_BUS = 0 + BUTTONS_BUS = 2 # camera controls ACC, need to send buttons on bus 2 + + TX_MSGS = [[0xE4, 0], [0x296, 2], [0x33D, 0]] + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} + + def setUp(self): + self.packer = CANPackerPanda("honda_civic_ex_2022_can_generated") + self.safety = libpanda_py.libpanda + + +class TestHondaBoschRadarlessSafety(HondaPcmEnableBase, TestHondaBoschRadarlessSafetyBase): + """ + Covers the Honda Bosch Radarless safety mode with stock longitudinal + """ + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, Panda.FLAG_HONDA_RADARLESS) + self.safety.init_tests() + + +class TestHondaBoschRadarlessAltBrakeSafety(HondaPcmEnableBase, TestHondaBoschRadarlessSafetyBase, TestHondaBoschAltBrakeSafetyBase): + """ + Covers the Honda Bosch Radarless safety mode with stock longitudinal and an alternate brake message + """ + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, Panda.FLAG_HONDA_RADARLESS | Panda.FLAG_HONDA_ALT_BRAKE) + self.safety.init_tests() + + +class TestHondaBoschRadarlessLongSafety(common.LongitudinalAccelSafetyTest, HondaButtonEnableBase, + TestHondaBoschRadarlessSafetyBase): + """ + Covers the Honda Bosch Radarless safety mode with longitudinal control + """ + TX_MSGS = [[0xE4, 0], [0x33D, 0], [0x1C8, 0], [0x30C, 0]] + FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB, 0x1C8, 0x30C]} + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_HONDA_BOSCH, Panda.FLAG_HONDA_RADARLESS | Panda.FLAG_HONDA_BOSCH_LONG) + self.safety.init_tests() + + def _accel_msg(self, accel): + values = { + "ACCEL_COMMAND": accel, + } + return self.packer.make_can_msg_panda("ACC_CONTROL", self.PT_BUS, values) + + # Longitudinal doesn't need to send buttons + def test_spam_cancel_safety_check(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_hyundai.py b/panda/tests/safety/test_hyundai.py new file mode 100644 index 0000000..fbe7d10 --- /dev/null +++ b/panda/tests/safety/test_hyundai.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +import random +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda +from panda.tests.safety.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase + + +# 4 bit checkusm used in some hyundai messages +# lives outside the can packer because we never send this msg +def checksum(msg): + addr, t, dat, bus = msg + + chksum = 0 + if addr == 0x386: + for i, b in enumerate(dat): + for j in range(8): + # exclude checksum and counter bits + if (i != 1 or j < 6) and (i != 3 or j < 6) and (i != 5 or j < 6) and (i != 7 or j < 6): + bit = (b >> j) & 1 + else: + bit = 0 + chksum += bit + chksum = (chksum ^ 9) & 0xF + ret = bytearray(dat) + ret[5] |= (chksum & 0x3) << 6 + ret[7] |= (chksum & 0xc) << 4 + else: + for i, b in enumerate(dat): + if addr in [0x260, 0x421] and i == 7: + b &= 0x0F if addr == 0x421 else 0xF0 + elif addr == 0x394 and i == 6: + b &= 0xF0 + elif addr == 0x394 and i == 7: + continue + chksum += sum(divmod(b, 16)) + chksum = (16 - chksum) % 16 + ret = bytearray(dat) + ret[6 if addr == 0x394 else 7] |= chksum << (4 if addr == 0x421 else 0) + + return addr, t, ret, bus + + +class TestHyundaiSafety(HyundaiButtonBase, common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): + TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0]] + STANDSTILL_THRESHOLD = 12 # 0.375 kph + RELAY_MALFUNCTION_ADDRS = {0: (0x340,)} # LKAS11 + FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + MAX_RATE_UP = 3 + MAX_RATE_DOWN = 7 + MAX_TORQUE = 384 + MAX_RT_DELTA = 112 + RT_INTERVAL = 250000 + DRIVER_TORQUE_ALLOWANCE = 50 + DRIVER_TORQUE_FACTOR = 2 + + # Safety around steering req bit + MIN_VALID_STEERING_FRAMES = 89 + MAX_INVALID_STEERING_FRAMES = 2 + MIN_VALID_STEERING_RT_INTERVAL = 810000 # a ~10% buffer, can send steer up to 110Hz + + cnt_gas = 0 + cnt_speed = 0 + cnt_brake = 0 + cnt_cruise = 0 + cnt_button = 0 + + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, 0) + self.safety.init_tests() + + def _button_msg(self, buttons, main_button=0, bus=0): + values = {"CF_Clu_CruiseSwState": buttons, "CF_Clu_CruiseSwMain": main_button, "CF_Clu_AliveCnt1": self.cnt_button} + self.__class__.cnt_button += 1 + return self.packer.make_can_msg_panda("CLU11", bus, values) + + def _user_gas_msg(self, gas): + values = {"CF_Ems_AclAct": gas, "AliveCounter": self.cnt_gas % 4} + self.__class__.cnt_gas += 1 + return self.packer.make_can_msg_panda("EMS16", 0, values, fix_checksum=checksum) + + def _user_brake_msg(self, brake): + values = {"DriverOverride": 2 if brake else random.choice((0, 1, 3)), + "AliveCounterTCS": self.cnt_brake % 8} + self.__class__.cnt_brake += 1 + return self.packer.make_can_msg_panda("TCS13", 0, values, fix_checksum=checksum) + + def _speed_msg(self, speed): + # panda safety doesn't scale, so undo the scaling + values = {"WHL_SPD_%s" % s: speed * 0.03125 for s in ["FL", "FR", "RL", "RR"]} + values["WHL_SPD_AliveCounter_LSB"] = (self.cnt_speed % 16) & 0x3 + values["WHL_SPD_AliveCounter_MSB"] = (self.cnt_speed % 16) >> 2 + self.__class__.cnt_speed += 1 + return self.packer.make_can_msg_panda("WHL_SPD11", 0, values, fix_checksum=checksum) + + def _pcm_status_msg(self, enable): + values = {"ACCMode": enable, "CR_VSM_Alive": self.cnt_cruise % 16} + self.__class__.cnt_cruise += 1 + return self.packer.make_can_msg_panda("SCC12", self.SCC_BUS, values, fix_checksum=checksum) + + def _torque_driver_msg(self, torque): + values = {"CR_Mdps_StrColTq": torque} + return self.packer.make_can_msg_panda("MDPS12", 0, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"CR_Lkas_StrToqReq": torque, "CF_Lkas_ActToi": steer_req} + return self.packer.make_can_msg_panda("LKAS11", 0, values) + + +class TestHyundaiSafetyAltLimits(TestHyundaiSafety): + MAX_RATE_UP = 2 + MAX_RATE_DOWN = 3 + MAX_TORQUE = 270 + + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_ALT_LIMITS) + self.safety.init_tests() + + +class TestHyundaiSafetyCameraSCC(TestHyundaiSafety): + BUTTONS_TX_BUS = 2 # tx on 2, rx on 0 + SCC_BUS = 2 # rx on 2 + + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_CAMERA_SCC) + self.safety.init_tests() + + +class TestHyundaiLegacySafety(TestHyundaiSafety): + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_LEGACY, 0) + self.safety.init_tests() + + +class TestHyundaiLegacySafetyEV(TestHyundaiSafety): + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_LEGACY, 1) + self.safety.init_tests() + + def _user_gas_msg(self, gas): + values = {"Accel_Pedal_Pos": gas} + return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) + + +class TestHyundaiLegacySafetyHEV(TestHyundaiSafety): + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_LEGACY, 2) + self.safety.init_tests() + + def _user_gas_msg(self, gas): + values = {"CR_Vcu_AccPedDep_Pos": gas} + return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) + +class TestHyundaiLongitudinalSafety(HyundaiLongitudinalBase, TestHyundaiSafety): + TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0], [0x38D, 0], [0x483, 0], [0x7D0, 0]] + + RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x421)} # LKAS11, SCC12 + + DISABLED_ECU_UDS_MSG = (0x7D0, 0) + DISABLED_ECU_ACTUATION_MSG = (0x421, 0) + + def setUp(self): + self.packer = CANPackerPanda("hyundai_kia_generic") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_LONG) + self.safety.init_tests() + + def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): + values = { + "aReqRaw": accel, + "aReqValue": accel, + "AEB_CmdAct": int(aeb_req), + "CR_VSM_DecCmd": aeb_decel, + } + return self.packer.make_can_msg_panda("SCC12", self.SCC_BUS, values) + + def _fca11_msg(self, idx=0, vsm_aeb_req=False, fca_aeb_req=False, aeb_decel=0): + values = { + "CR_FCA_Alive": idx % 0xF, + "FCA_Status": 2, + "CR_VSM_DecCmd": aeb_decel, + "CF_VSM_DecCmdAct": int(vsm_aeb_req), + "FCA_CmdAct": int(fca_aeb_req), + } + return self.packer.make_can_msg_panda("FCA11", 0, values) + + def test_no_aeb_fca11(self): + self.assertTrue(self._tx(self._fca11_msg())) + self.assertFalse(self._tx(self._fca11_msg(vsm_aeb_req=True))) + self.assertFalse(self._tx(self._fca11_msg(fca_aeb_req=True))) + self.assertFalse(self._tx(self._fca11_msg(aeb_decel=1.0))) + + def test_no_aeb_scc12(self): + self.assertTrue(self._tx(self._accel_msg(0))) + self.assertFalse(self._tx(self._accel_msg(0, aeb_req=True))) + self.assertFalse(self._tx(self._accel_msg(0, aeb_decel=1.0))) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_hyundai_canfd.py b/panda/tests/safety/test_hyundai_canfd.py new file mode 100644 index 0000000..7f280b6 --- /dev/null +++ b/panda/tests/safety/test_hyundai_canfd.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +from parameterized import parameterized_class +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda +from panda.tests.safety.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase + + +class TestHyundaiCanfdBase(HyundaiButtonBase, common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): + + TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]] + STANDSTILL_THRESHOLD = 12 # 0.375 kph + FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + MAX_RATE_UP = 2 + MAX_RATE_DOWN = 3 + MAX_TORQUE = 270 + + MAX_RT_DELTA = 112 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 250 + DRIVER_TORQUE_FACTOR = 2 + + # Safety around steering req bit + MIN_VALID_STEERING_FRAMES = 89 + MAX_INVALID_STEERING_FRAMES = 2 + MIN_VALID_STEERING_RT_INTERVAL = 810000 # a ~10% buffer, can send steer up to 110Hz + + PT_BUS = 0 + SCC_BUS = 2 + STEER_BUS = 0 + STEER_MSG = "" + GAS_MSG = ("", "") + BUTTONS_TX_BUS = 1 + + @classmethod + def setUpClass(cls): + super().setUpClass() + if cls.__name__ == "TestHyundaiCanfdBase": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _torque_driver_msg(self, torque): + values = {"STEERING_COL_TORQUE": torque} + return self.packer.make_can_msg_panda("MDPS", self.PT_BUS, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"TORQUE_REQUEST": torque, "STEER_REQ": steer_req} + return self.packer.make_can_msg_panda(self.STEER_MSG, self.STEER_BUS, values) + + def _speed_msg(self, speed): + values = {f"WHEEL_SPEED_{i}": speed * 0.03125 for i in range(1, 5)} + return self.packer.make_can_msg_panda("WHEEL_SPEEDS", self.PT_BUS, values) + + def _user_brake_msg(self, brake): + values = {"DriverBraking": brake} + return self.packer.make_can_msg_panda("TCS", self.PT_BUS, values) + + def _user_gas_msg(self, gas): + values = {self.GAS_MSG[1]: gas} + return self.packer.make_can_msg_panda(self.GAS_MSG[0], self.PT_BUS, values) + + def _pcm_status_msg(self, enable): + values = {"ACCMode": 1 if enable else 0} + return self.packer.make_can_msg_panda("SCC_CONTROL", self.SCC_BUS, values) + + def _button_msg(self, buttons, main_button=0, bus=None): + if bus is None: + bus = self.PT_BUS + values = { + "CRUISE_BUTTONS": buttons, + "ADAPTIVE_CRUISE_MAIN_BTN": main_button, + } + return self.packer.make_can_msg_panda("CRUISE_BUTTONS", bus, values) + + +class TestHyundaiCanfdHDA1Base(TestHyundaiCanfdBase): + + TX_MSGS = [[0x12A, 0], [0x1A0, 1], [0x1CF, 0], [0x1E0, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x12A,)} # LFA + FWD_BLACKLISTED_ADDRS = {2: [0x12A, 0x1E0]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + STEER_MSG = "LFA" + BUTTONS_TX_BUS = 2 + SAFETY_PARAM: int + + @classmethod + def setUpClass(cls): + super().setUpClass() + if cls.__name__ in ("TestHyundaiCanfdHDA1", "TestHyundaiCanfdHDA1AltButtons"): + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, self.SAFETY_PARAM) + self.safety.init_tests() + + +@parameterized_class([ + # Radar SCC, test with long flag to ensure flag is not respected until it is supported + {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_LONG}, + {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_EV_GAS | Panda.FLAG_HYUNDAI_LONG}, + {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_HYBRID_GAS | Panda.FLAG_HYUNDAI_LONG}, + # Camera SCC + {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_CAMERA_SCC}, + {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_EV_GAS | Panda.FLAG_HYUNDAI_CAMERA_SCC}, + {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_HYBRID_GAS | Panda.FLAG_HYUNDAI_CAMERA_SCC}, +]) +class TestHyundaiCanfdHDA1(TestHyundaiCanfdHDA1Base): + pass + + +@parameterized_class([ + # Radar SCC, test with long flag to ensure flag is not respected until it is supported + {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_LONG}, + {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_EV_GAS | Panda.FLAG_HYUNDAI_LONG}, + {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_HYBRID_GAS | Panda.FLAG_HYUNDAI_LONG}, + # Camera SCC + {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_CAMERA_SCC}, + {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_EV_GAS | Panda.FLAG_HYUNDAI_CAMERA_SCC}, + {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": Panda.FLAG_HYUNDAI_HYBRID_GAS | Panda.FLAG_HYUNDAI_CAMERA_SCC}, +]) +class TestHyundaiCanfdHDA1AltButtons(TestHyundaiCanfdHDA1Base): + + SAFETY_PARAM: int + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS | self.SAFETY_PARAM) + self.safety.init_tests() + + def _button_msg(self, buttons, main_button=0, bus=1): + values = { + "CRUISE_BUTTONS": buttons, + "ADAPTIVE_CRUISE_MAIN_BTN": main_button, + } + return self.packer.make_can_msg_panda("CRUISE_BUTTONS_ALT", self.PT_BUS, values) + + def test_button_sends(self): + """ + No button send allowed with alt buttons. + """ + for enabled in (True, False): + for btn in range(8): + self.safety.set_controls_allowed(enabled) + self.assertFalse(self._tx(self._button_msg(btn))) + + +class TestHyundaiCanfdHDA2EV(TestHyundaiCanfdBase): + + TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x50,)} # LKAS + FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + PT_BUS = 1 + SCC_BUS = 1 + STEER_MSG = "LKAS" + GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, Panda.FLAG_HYUNDAI_CANFD_HDA2 | Panda.FLAG_HYUNDAI_EV_GAS) + self.safety.init_tests() + + +# TODO: Handle ICE and HEV configurations once we see cars that use the new messages +class TestHyundaiCanfdHDA2EVAltSteering(TestHyundaiCanfdBase): + + TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x362, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x110,)} # LKAS_ALT + FWD_BLACKLISTED_ADDRS = {2: [0x110, 0x362]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + PT_BUS = 1 + SCC_BUS = 1 + STEER_MSG = "LKAS_ALT" + GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, Panda.FLAG_HYUNDAI_CANFD_HDA2 | Panda.FLAG_HYUNDAI_EV_GAS | + Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING) + self.safety.init_tests() + + +class TestHyundaiCanfdHDA2LongEV(HyundaiLongitudinalBase, TestHyundaiCanfdHDA2EV): + + TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0], [0x51, 0], [0x730, 1], [0x12a, 1], [0x160, 1], + [0x1e0, 1], [0x1a0, 1], [0x1ea, 1], [0x200, 1], [0x345, 1], [0x1da, 1]] + + RELAY_MALFUNCTION_ADDRS = {0: (0x50,), 1: (0x1a0,)} # LKAS, SCC_CONTROL + + DISABLED_ECU_UDS_MSG = (0x730, 1) + DISABLED_ECU_ACTUATION_MSG = (0x1a0, 1) + + STEER_MSG = "LFA" + GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") + STEER_BUS = 1 + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, Panda.FLAG_HYUNDAI_CANFD_HDA2 | Panda.FLAG_HYUNDAI_LONG | Panda.FLAG_HYUNDAI_EV_GAS) + self.safety.init_tests() + + def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): + values = { + "aReqRaw": accel, + "aReqValue": accel, + } + return self.packer.make_can_msg_panda("SCC_CONTROL", 1, values) + + +# Tests HDA1 longitudinal for ICE, hybrid, EV +@parameterized_class([ + # Camera SCC is the only supported configuration for HDA1 longitudinal, TODO: allow radar SCC + {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SAFETY_PARAM": Panda.FLAG_HYUNDAI_LONG}, + {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SAFETY_PARAM": Panda.FLAG_HYUNDAI_LONG | Panda.FLAG_HYUNDAI_EV_GAS}, + {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SAFETY_PARAM": Panda.FLAG_HYUNDAI_LONG | Panda.FLAG_HYUNDAI_HYBRID_GAS}, +]) +class TestHyundaiCanfdHDA1Long(HyundaiLongitudinalBase, TestHyundaiCanfdHDA1Base): + + FWD_BLACKLISTED_ADDRS = {2: [0x12a, 0x1e0, 0x1a0]} + + RELAY_MALFUNCTION_ADDRS = {0: (0x12A, 0x1a0)} # LFA, SCC_CONTROL + + DISABLED_ECU_UDS_MSG = (0x730, 1) + DISABLED_ECU_ACTUATION_MSG = (0x1a0, 0) + + STEER_MSG = "LFA" + STEER_BUS = 0 + SCC_BUS = 2 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestHyundaiCanfdHDA1Long": + cls.safety = None + raise unittest.SkipTest + + def setUp(self): + self.packer = CANPackerPanda("hyundai_canfd") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI_CANFD, Panda.FLAG_HYUNDAI_CAMERA_SCC | self.SAFETY_PARAM) + self.safety.init_tests() + + def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): + values = { + "aReqRaw": accel, + "aReqValue": accel, + } + return self.packer.make_can_msg_panda("SCC_CONTROL", 0, values) + + # no knockout + def test_tester_present_allowed(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_mazda.py b/panda/tests/safety/test_mazda.py new file mode 100644 index 0000000..9d2fb89 --- /dev/null +++ b/panda/tests/safety/test_mazda.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + + +class TestMazdaSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): + + TX_MSGS = [[0x243, 0], [0x09d, 0], [0x440, 0]] + STANDSTILL_THRESHOLD = .1 + RELAY_MALFUNCTION_ADDRS = {0: (0x243,)} + FWD_BLACKLISTED_ADDRS = {2: [0x243, 0x440]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + MAX_RATE_UP = 10 + MAX_RATE_DOWN = 25 + MAX_TORQUE = 800 + + MAX_RT_DELTA = 300 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 15 + DRIVER_TORQUE_FACTOR = 1 + + # Mazda actually does not set any bit when requesting torque + NO_STEER_REQ_BIT = True + + def setUp(self): + self.packer = CANPackerPanda("mazda_2017") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_MAZDA, 0) + self.safety.init_tests() + + def _torque_meas_msg(self, torque): + values = {"STEER_TORQUE_MOTOR": torque} + return self.packer.make_can_msg_panda("STEER_TORQUE", 0, values) + + def _torque_driver_msg(self, torque): + values = {"STEER_TORQUE_SENSOR": torque} + return self.packer.make_can_msg_panda("STEER_TORQUE", 0, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"LKAS_REQUEST": torque} + return self.packer.make_can_msg_panda("CAM_LKAS", 0, values) + + def _speed_msg(self, speed): + values = {"SPEED": speed} + return self.packer.make_can_msg_panda("ENGINE_DATA", 0, values) + + def _user_brake_msg(self, brake): + values = {"BRAKE_ON": brake} + return self.packer.make_can_msg_panda("PEDALS", 0, values) + + def _user_gas_msg(self, gas): + values = {"PEDAL_GAS": gas} + return self.packer.make_can_msg_panda("ENGINE_DATA", 0, values) + + def _pcm_status_msg(self, enable): + values = {"CRZ_ACTIVE": enable} + return self.packer.make_can_msg_panda("CRZ_CTRL", 0, values) + + def _button_msg(self, resume=False, cancel=False): + values = { + "CAN_OFF": cancel, + "CAN_OFF_INV": (cancel + 1) % 2, + "RES": resume, + "RES_INV": (resume + 1) % 2, + } + return self.packer.make_can_msg_panda("CRZ_BTNS", 0, values) + + def test_buttons(self): + # only cancel allows while controls not allowed + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._button_msg(cancel=True))) + self.assertFalse(self._tx(self._button_msg(resume=True))) + + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._button_msg(cancel=True))) + self.assertTrue(self._tx(self._button_msg(resume=True))) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_nissan.py b/panda/tests/safety/test_nissan.py new file mode 100644 index 0000000..4c83ca3 --- /dev/null +++ b/panda/tests/safety/test_nissan.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + + +class TestNissanSafety(common.PandaCarSafetyTest, common.AngleSteeringSafetyTest): + + TX_MSGS = [[0x169, 0], [0x2b1, 0], [0x4cc, 0], [0x20b, 2], [0x280, 2]] + STANDSTILL_THRESHOLD = 0 + GAS_PRESSED_THRESHOLD = 3 + RELAY_MALFUNCTION_ADDRS = {0: (0x169,)} + FWD_BLACKLISTED_ADDRS = {0: [0x280], 2: [0x169, 0x2b1, 0x4cc]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + EPS_BUS = 0 + CRUISE_BUS = 2 + + # Angle control limits + DEG_TO_CAN = 100 + + ANGLE_RATE_BP = [0., 5., 15.] + ANGLE_RATE_UP = [5., .8, .15] # windup limit + ANGLE_RATE_DOWN = [5., 3.5, .4] # unwind limit + + def setUp(self): + self.packer = CANPackerPanda("nissan_x_trail_2017_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_NISSAN, 0) + self.safety.init_tests() + + def _angle_cmd_msg(self, angle: float, enabled: bool): + values = {"DESIRED_ANGLE": angle, "LKA_ACTIVE": 1 if enabled else 0} + return self.packer.make_can_msg_panda("LKAS", 0, values) + + def _angle_meas_msg(self, angle: float): + values = {"STEER_ANGLE": angle} + return self.packer.make_can_msg_panda("STEER_ANGLE_SENSOR", self.EPS_BUS, values) + + def _pcm_status_msg(self, enable): + values = {"CRUISE_ENABLED": enable} + return self.packer.make_can_msg_panda("CRUISE_STATE", self.CRUISE_BUS, values) + + def _speed_msg(self, speed): + values = {"WHEEL_SPEED_%s" % s: speed * 3.6 for s in ["RR", "RL"]} + return self.packer.make_can_msg_panda("WHEEL_SPEEDS_REAR", self.EPS_BUS, values) + + def _user_brake_msg(self, brake): + values = {"USER_BRAKE_PRESSED": brake} + return self.packer.make_can_msg_panda("DOORS_LIGHTS", self.EPS_BUS, values) + + def _user_gas_msg(self, gas): + values = {"GAS_PEDAL": gas} + return self.packer.make_can_msg_panda("GAS_PEDAL", self.EPS_BUS, values) + + def _acc_button_cmd(self, cancel=0, propilot=0, flw_dist=0, _set=0, res=0): + no_button = not any([cancel, propilot, flw_dist, _set, res]) + values = {"CANCEL_BUTTON": cancel, "PROPILOT_BUTTON": propilot, + "FOLLOW_DISTANCE_BUTTON": flw_dist, "SET_BUTTON": _set, + "RES_BUTTON": res, "NO_BUTTON_PRESSED": no_button} + return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 2, values) + + def test_acc_buttons(self): + btns = [ + ("cancel", True), + ("propilot", False), + ("flw_dist", False), + ("_set", False), + ("res", False), + (None, False), + ] + for controls_allowed in (True, False): + for btn, should_tx in btns: + self.safety.set_controls_allowed(controls_allowed) + args = {} if btn is None else {btn: 1} + tx = self._tx(self._acc_button_cmd(**args)) + self.assertEqual(tx, should_tx) + + +class TestNissanSafetyAltEpsBus(TestNissanSafety): + """Altima uses different buses""" + + EPS_BUS = 1 + CRUISE_BUS = 1 + + def setUp(self): + self.packer = CANPackerPanda("nissan_x_trail_2017_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_NISSAN, Panda.FLAG_NISSAN_ALT_EPS_BUS) + self.safety.init_tests() + + +class TestNissanLeafSafety(TestNissanSafety): + + def setUp(self): + self.packer = CANPackerPanda("nissan_leaf_2018_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_NISSAN, 0) + self.safety.init_tests() + + def _user_brake_msg(self, brake): + values = {"USER_BRAKE_PRESSED": brake} + return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 0, values) + + def _user_gas_msg(self, gas): + values = {"GAS_PEDAL": gas} + return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 0, values) + + # TODO: leaf should use its own safety param + def test_acc_buttons(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_subaru.py b/panda/tests/safety/test_subaru.py new file mode 100644 index 0000000..61d3d91 --- /dev/null +++ b/panda/tests/safety/test_subaru.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +import enum +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda +from functools import partial + +class SubaruMsg(enum.IntEnum): + Brake_Status = 0x13c + CruiseControl = 0x240 + Throttle = 0x40 + Steering_Torque = 0x119 + Wheel_Speeds = 0x13a + ES_LKAS = 0x122 + ES_LKAS_ANGLE = 0x124 + ES_Brake = 0x220 + ES_Distance = 0x221 + ES_Status = 0x222 + ES_DashStatus = 0x321 + ES_LKAS_State = 0x322 + ES_Infotainment = 0x323 + ES_UDS_Request = 0x787 + ES_HighBeamAssist = 0x121 + ES_STATIC_1 = 0x22a + ES_STATIC_2 = 0x325 + + +SUBARU_MAIN_BUS = 0 +SUBARU_ALT_BUS = 1 +SUBARU_CAM_BUS = 2 + + +def lkas_tx_msgs(alt_bus, lkas_msg=SubaruMsg.ES_LKAS): + return [[lkas_msg, SUBARU_MAIN_BUS], + [SubaruMsg.ES_Distance, alt_bus], + [SubaruMsg.ES_DashStatus, SUBARU_MAIN_BUS], + [SubaruMsg.ES_LKAS_State, SUBARU_MAIN_BUS], + [SubaruMsg.ES_Infotainment, SUBARU_MAIN_BUS]] + +def long_tx_msgs(alt_bus): + return [[SubaruMsg.ES_Brake, alt_bus], + [SubaruMsg.ES_Status, alt_bus]] + +def gen2_long_additional_tx_msgs(): + return [[SubaruMsg.ES_UDS_Request, SUBARU_CAM_BUS], + [SubaruMsg.ES_HighBeamAssist, SUBARU_MAIN_BUS], + [SubaruMsg.ES_STATIC_1, SUBARU_MAIN_BUS], + [SubaruMsg.ES_STATIC_2, SUBARU_MAIN_BUS]] + +def fwd_blacklisted_addr(lkas_msg=SubaruMsg.ES_LKAS): + return {SUBARU_CAM_BUS: [lkas_msg, SubaruMsg.ES_DashStatus, SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment]} + +class TestSubaruSafetyBase(common.PandaCarSafetyTest): + FLAGS = 0 + STANDSTILL_THRESHOLD = 0 # kph + RELAY_MALFUNCTION_ADDRS = {SUBARU_MAIN_BUS: (SubaruMsg.ES_LKAS,)} + FWD_BUS_LOOKUP = {SUBARU_MAIN_BUS: SUBARU_CAM_BUS, SUBARU_CAM_BUS: SUBARU_MAIN_BUS} + FWD_BLACKLISTED_ADDRS = fwd_blacklisted_addr() + + MAX_RT_DELTA = 940 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 60 + DRIVER_TORQUE_FACTOR = 50 + + ALT_MAIN_BUS = SUBARU_MAIN_BUS + ALT_CAM_BUS = SUBARU_CAM_BUS + + DEG_TO_CAN = 100 + + INACTIVE_GAS = 1818 + + def setUp(self): + self.packer = CANPackerPanda("subaru_global_2017_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_SUBARU, self.FLAGS) + self.safety.init_tests() + + def _set_prev_torque(self, t): + self.safety.set_desired_torque_last(t) + self.safety.set_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + values = {"Steer_Torque_Sensor": torque} + return self.packer.make_can_msg_panda("Steering_Torque", 0, values) + + def _speed_msg(self, speed): + values = {s: speed for s in ["FR", "FL", "RR", "RL"]} + return self.packer.make_can_msg_panda("Wheel_Speeds", self.ALT_MAIN_BUS, values) + + def _angle_meas_msg(self, angle): + values = {"Steering_Angle": angle} + return self.packer.make_can_msg_panda("Steering_Torque", 0, values) + + def _user_brake_msg(self, brake): + values = {"Brake": brake} + return self.packer.make_can_msg_panda("Brake_Status", self.ALT_MAIN_BUS, values) + + def _user_gas_msg(self, gas): + values = {"Throttle_Pedal": gas} + return self.packer.make_can_msg_panda("Throttle", 0, values) + + def _pcm_status_msg(self, enable): + values = {"Cruise_Activated": enable} + return self.packer.make_can_msg_panda("CruiseControl", self.ALT_MAIN_BUS, values) + + +class TestSubaruStockLongitudinalSafetyBase(TestSubaruSafetyBase): + def _cancel_msg(self, cancel, cruise_throttle=0): + values = {"Cruise_Cancel": cancel, "Cruise_Throttle": cruise_throttle} + return self.packer.make_can_msg_panda("ES_Distance", self.ALT_MAIN_BUS, values) + + def test_cancel_message(self): + # test that we can only send the cancel message (ES_Distance) with inactive throttle (1818) and Cruise_Cancel=1 + for cancel in [True, False]: + self._generic_limit_safety_check(partial(self._cancel_msg, cancel), self.INACTIVE_GAS, self.INACTIVE_GAS, 0, 2**12, 1, self.INACTIVE_GAS, cancel) + + +class TestSubaruLongitudinalSafetyBase(TestSubaruSafetyBase, common.LongitudinalGasBrakeSafetyTest): + MIN_GAS = 808 + MAX_GAS = 3400 + INACTIVE_GAS = 1818 + MAX_POSSIBLE_GAS = 2**13 + + MIN_BRAKE = 0 + MAX_BRAKE = 600 + MAX_POSSIBLE_BRAKE = 2**16 + + MIN_RPM = 0 + MAX_RPM = 2400 + MAX_POSSIBLE_RPM = 2**13 + + FWD_BLACKLISTED_ADDRS = {2: [SubaruMsg.ES_LKAS, SubaruMsg.ES_Brake, SubaruMsg.ES_Distance, + SubaruMsg.ES_Status, SubaruMsg.ES_DashStatus, + SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment]} + + def test_rpm_safety_check(self): + self._generic_limit_safety_check(self._send_rpm_msg, self.MIN_RPM, self.MAX_RPM, 0, self.MAX_POSSIBLE_RPM, 1) + + def _send_brake_msg(self, brake): + values = {"Brake_Pressure": brake} + return self.packer.make_can_msg_panda("ES_Brake", self.ALT_MAIN_BUS, values) + + def _send_gas_msg(self, gas): + values = {"Cruise_Throttle": gas} + return self.packer.make_can_msg_panda("ES_Distance", self.ALT_MAIN_BUS, values) + + def _send_rpm_msg(self, rpm): + values = {"Cruise_RPM": rpm} + return self.packer.make_can_msg_panda("ES_Status", self.ALT_MAIN_BUS, values) + + +class TestSubaruTorqueSafetyBase(TestSubaruSafetyBase, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): + MAX_RATE_UP = 50 + MAX_RATE_DOWN = 70 + MAX_TORQUE = 2047 + + # Safety around steering req bit + MIN_VALID_STEERING_FRAMES = 7 + MAX_INVALID_STEERING_FRAMES = 1 + MIN_VALID_STEERING_RT_INTERVAL = 144000 + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"LKAS_Output": torque, "LKAS_Request": steer_req} + return self.packer.make_can_msg_panda("ES_LKAS", SUBARU_MAIN_BUS, values) + + +class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): + FLAGS = 0 + TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) + + +class TestSubaruGen2TorqueSafetyBase(TestSubaruTorqueSafetyBase): + ALT_MAIN_BUS = SUBARU_ALT_BUS + ALT_CAM_BUS = SUBARU_ALT_BUS + + MAX_RATE_UP = 40 + MAX_RATE_DOWN = 40 + MAX_TORQUE = 1000 + + +class TestSubaruGen2TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): + FLAGS = Panda.FLAG_SUBARU_GEN2 + TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS) + + +class TestSubaruGen1LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): + FLAGS = Panda.FLAG_SUBARU_LONG + TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) + long_tx_msgs(SUBARU_MAIN_BUS) + + +class TestSubaruGen2LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): + FLAGS = Panda.FLAG_SUBARU_LONG | Panda.FLAG_SUBARU_GEN2 + TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS) + long_tx_msgs(SUBARU_ALT_BUS) + gen2_long_additional_tx_msgs() + + def _rdbi_msg(self, did: int): + return b'\x03\x22' + did.to_bytes(2) + b'\x00\x00\x00\x00' + + def _es_uds_msg(self, msg: bytes): + return libpanda_py.make_CANPacket(SubaruMsg.ES_UDS_Request, 2, msg) + + def test_es_uds_message(self): + tester_present = b'\x02\x3E\x80\x00\x00\x00\x00\x00' + not_tester_present = b"\x03\xAA\xAA\x00\x00\x00\x00\x00" + + button_did = 0x1130 + + # Tester present is allowed for gen2 long to keep eyesight disabled + self.assertTrue(self._tx(self._es_uds_msg(tester_present))) + + # Non-Tester present is not allowed + self.assertFalse(self._tx(self._es_uds_msg(not_tester_present))) + + # Only button_did is allowed to be read via UDS + for did in range(0xFFFF): + should_tx = (did == button_did) + self.assertEqual(self._tx(self._es_uds_msg(self._rdbi_msg(did))), should_tx) + + # any other msg is not allowed + for sid in range(0xFF): + msg = b'\x03' + sid.to_bytes(1) + b'\x00' * 6 + self.assertFalse(self._tx(self._es_uds_msg(msg))) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_subaru_preglobal.py b/panda/tests/safety/test_subaru_preglobal.py new file mode 100644 index 0000000..06c4cde --- /dev/null +++ b/panda/tests/safety/test_subaru_preglobal.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + + +class TestSubaruPreglobalSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): + FLAGS = 0 + DBC = "subaru_outback_2015_generated" + TX_MSGS = [[0x161, 0], [0x164, 0]] + STANDSTILL_THRESHOLD = 0 # kph + RELAY_MALFUNCTION_ADDRS = {0: (0x164,)} + FWD_BLACKLISTED_ADDRS = {2: [0x161, 0x164]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + MAX_RATE_UP = 50 + MAX_RATE_DOWN = 70 + MAX_TORQUE = 2047 + + MAX_RT_DELTA = 940 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 75 + DRIVER_TORQUE_FACTOR = 10 + + def setUp(self): + self.packer = CANPackerPanda(self.DBC) + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_SUBARU_PREGLOBAL, self.FLAGS) + self.safety.init_tests() + + def _set_prev_torque(self, t): + self.safety.set_desired_torque_last(t) + self.safety.set_rt_torque_last(t) + + def _torque_driver_msg(self, torque): + values = {"Steer_Torque_Sensor": torque} + return self.packer.make_can_msg_panda("Steering_Torque", 0, values) + + def _speed_msg(self, speed): + # subaru safety doesn't use the scaled value, so undo the scaling + values = {s: speed*0.0592 for s in ["FR", "FL", "RR", "RL"]} + return self.packer.make_can_msg_panda("Wheel_Speeds", 0, values) + + def _user_brake_msg(self, brake): + values = {"Brake_Pedal": brake} + return self.packer.make_can_msg_panda("Brake_Pedal", 0, values) + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"LKAS_Command": torque, "LKAS_Active": steer_req} + return self.packer.make_can_msg_panda("ES_LKAS", 0, values) + + def _user_gas_msg(self, gas): + values = {"Throttle_Pedal": gas} + return self.packer.make_can_msg_panda("Throttle", 0, values) + + def _pcm_status_msg(self, enable): + values = {"Cruise_Activated": enable} + return self.packer.make_can_msg_panda("CruiseControl", 0, values) + + +class TestSubaruPreglobalReversedDriverTorqueSafety(TestSubaruPreglobalSafety): + FLAGS = Panda.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE + DBC = "subaru_outback_2019_generated" + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_tesla.py b/panda/tests/safety/test_tesla.py new file mode 100644 index 0000000..9461ff6 --- /dev/null +++ b/panda/tests/safety/test_tesla.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +import unittest +import numpy as np +from panda import Panda +import panda.tests.safety.common as common +from panda.tests.libpanda import libpanda_py +from panda.tests.safety.common import CANPackerPanda + +MAX_ACCEL = 2.0 +MIN_ACCEL = -3.5 + + +class CONTROL_LEVER_STATE: + DN_1ST = 32 + UP_1ST = 16 + DN_2ND = 8 + UP_2ND = 4 + RWD = 2 + FWD = 1 + IDLE = 0 + + +class TestTeslaSafety(common.PandaCarSafetyTest): + STANDSTILL_THRESHOLD = 0 + GAS_PRESSED_THRESHOLD = 3 + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + def setUp(self): + self.packer = None + raise unittest.SkipTest + + def _speed_msg(self, speed): + values = {"DI_vehicleSpeed": speed / 0.447} + return self.packer.make_can_msg_panda("DI_torque2", 0, values) + + def _user_brake_msg(self, brake): + values = {"driverBrakeStatus": 2 if brake else 1} + return self.packer.make_can_msg_panda("BrakeMessage", 0, values) + + def _user_gas_msg(self, gas): + values = {"DI_pedalPos": gas} + return self.packer.make_can_msg_panda("DI_torque1", 0, values) + + def _control_lever_cmd(self, command): + values = {"SpdCtrlLvr_Stat": command} + return self.packer.make_can_msg_panda("STW_ACTN_RQ", 0, values) + + def _pcm_status_msg(self, enable): + values = {"DI_cruiseState": 2 if enable else 0} + return self.packer.make_can_msg_panda("DI_state", 0, values) + + def _long_control_msg(self, set_speed, acc_val=0, jerk_limits=(0, 0), accel_limits=(0, 0), aeb_event=0, bus=0): + values = { + "DAS_setSpeed": set_speed, + "DAS_accState": acc_val, + "DAS_aebEvent": aeb_event, + "DAS_jerkMin": jerk_limits[0], + "DAS_jerkMax": jerk_limits[1], + "DAS_accelMin": accel_limits[0], + "DAS_accelMax": accel_limits[1], + } + return self.packer.make_can_msg_panda("DAS_control", bus, values) + + +class TestTeslaSteeringSafety(TestTeslaSafety, common.AngleSteeringSafetyTest): + TX_MSGS = [[0x488, 0], [0x45, 0], [0x45, 2]] + RELAY_MALFUNCTION_ADDRS = {0: (0x488,)} + FWD_BLACKLISTED_ADDRS = {2: [0x488]} + + # Angle control limits + DEG_TO_CAN = 10 + + ANGLE_RATE_BP = [0., 5., 15.] + ANGLE_RATE_UP = [10., 1.6, .3] # windup limit + ANGLE_RATE_DOWN = [10., 7.0, .8] # unwind limit + + def setUp(self): + self.packer = CANPackerPanda("tesla_can") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TESLA, 0) + self.safety.init_tests() + + def _angle_cmd_msg(self, angle: float, enabled: bool): + values = {"DAS_steeringAngleRequest": angle, "DAS_steeringControlType": 1 if enabled else 0} + return self.packer.make_can_msg_panda("DAS_steeringControl", 0, values) + + def _angle_meas_msg(self, angle: float): + values = {"EPAS_internalSAS": angle} + return self.packer.make_can_msg_panda("EPAS_sysStatus", 0, values) + + def test_acc_buttons(self): + """ + FWD (cancel) always allowed. + """ + btns = [ + (CONTROL_LEVER_STATE.FWD, True), + (CONTROL_LEVER_STATE.RWD, False), + (CONTROL_LEVER_STATE.UP_1ST, False), + (CONTROL_LEVER_STATE.UP_2ND, False), + (CONTROL_LEVER_STATE.DN_1ST, False), + (CONTROL_LEVER_STATE.DN_2ND, False), + (CONTROL_LEVER_STATE.IDLE, False), + ] + for btn, should_tx in btns: + for controls_allowed in (True, False): + self.safety.set_controls_allowed(controls_allowed) + tx = self._tx(self._control_lever_cmd(btn)) + self.assertEqual(tx, should_tx) + + +class TestTeslaRavenSteeringSafety(TestTeslaSteeringSafety): + def setUp(self): + self.packer = CANPackerPanda("tesla_can") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TESLA, Panda.FLAG_TESLA_RAVEN) + self.safety.init_tests() + + def _angle_meas_msg(self, angle: float): + values = {"EPAS_internalSAS": angle} + return self.packer.make_can_msg_panda("EPAS3P_sysStatus", 2, values) + +class TestTeslaLongitudinalSafety(TestTeslaSafety): + def setUp(self): + raise unittest.SkipTest + + def test_no_aeb(self): + for aeb_event in range(4): + self.assertEqual(self._tx(self._long_control_msg(10, aeb_event=aeb_event)), aeb_event == 0) + + def test_stock_aeb_passthrough(self): + no_aeb_msg = self._long_control_msg(10, aeb_event=0) + no_aeb_msg_cam = self._long_control_msg(10, aeb_event=0, bus=2) + aeb_msg_cam = self._long_control_msg(10, aeb_event=1, bus=2) + + # stock system sends no AEB -> no forwarding, and OP is allowed to TX + self.assertEqual(1, self._rx(no_aeb_msg_cam)) + self.assertEqual(-1, self.safety.safety_fwd_hook(2, no_aeb_msg_cam.addr)) + self.assertEqual(True, self._tx(no_aeb_msg)) + + # stock system sends AEB -> forwarding, and OP is not allowed to TX + self.assertEqual(1, self._rx(aeb_msg_cam)) + self.assertEqual(0, self.safety.safety_fwd_hook(2, aeb_msg_cam.addr)) + self.assertEqual(False, self._tx(no_aeb_msg)) + + def test_acc_accel_limits(self): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + for min_accel in np.arange(MIN_ACCEL - 1, MAX_ACCEL + 1, 0.1): + for max_accel in np.arange(MIN_ACCEL - 1, MAX_ACCEL + 1, 0.1): + # floats might not hit exact boundary conditions without rounding + min_accel = round(min_accel, 2) + max_accel = round(max_accel, 2) + if controls_allowed: + send = (MIN_ACCEL <= min_accel <= MAX_ACCEL) and (MIN_ACCEL <= max_accel <= MAX_ACCEL) + else: + send = np.all(np.isclose([min_accel, max_accel], 0, atol=0.0001)) + self.assertEqual(send, self._tx(self._long_control_msg(10, acc_val=4, accel_limits=[min_accel, max_accel]))) + + +class TestTeslaChassisLongitudinalSafety(TestTeslaLongitudinalSafety): + TX_MSGS = [[0x488, 0], [0x45, 0], [0x45, 2], [0x2B9, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x488,)} + FWD_BLACKLISTED_ADDRS = {2: [0x2B9, 0x488]} + + def setUp(self): + self.packer = CANPackerPanda("tesla_can") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TESLA, Panda.FLAG_TESLA_LONG_CONTROL) + self.safety.init_tests() + + +class TestTeslaPTLongitudinalSafety(TestTeslaLongitudinalSafety): + TX_MSGS = [[0x2BF, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x2BF,)} + FWD_BLACKLISTED_ADDRS = {2: [0x2BF]} + + def setUp(self): + self.packer = CANPackerPanda("tesla_powertrain") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TESLA, Panda.FLAG_TESLA_LONG_CONTROL | Panda.FLAG_TESLA_POWERTRAIN) + self.safety.init_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_toyota.py b/panda/tests/safety/test_toyota.py new file mode 100644 index 0000000..0743c67 --- /dev/null +++ b/panda/tests/safety/test_toyota.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python3 +import numpy as np +import random +import unittest +import itertools + +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + +TOYOTA_COMMON_TX_MSGS = [[0x2E4, 0], [0x191, 0], [0x412, 0], [0x343, 0], [0x1D2, 0]] # LKAS + LTA + ACC & PCM cancel cmds +TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0 + [0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1 + [0x411, 0], # PCS_HUD + [0x750, 0]] # radar diagnostic address +GAS_INTERCEPTOR_TX_MSGS = [[0x200, 0]] + + +class TestToyotaSafetyBase(common.PandaCarSafetyTest, common.LongitudinalAccelSafetyTest): + + TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS + STANDSTILL_THRESHOLD = 0 # kph + RELAY_MALFUNCTION_ADDRS = {0: (0x2E4, 0x343)} + FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x343]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + EPS_SCALE = 73 + + packer: CANPackerPanda + safety: libpanda_py.Panda + + @classmethod + def setUpClass(cls): + if cls.__name__.endswith("Base"): + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _torque_meas_msg(self, torque: int, driver_torque: int | None = None): + values = {"STEER_TORQUE_EPS": (torque / self.EPS_SCALE) * 100.} + if driver_torque is not None: + values["STEER_TORQUE_DRIVER"] = driver_torque + return self.packer.make_can_msg_panda("STEER_TORQUE_SENSOR", 0, values) + + # Both torque and angle safety modes test with each other's steering commands + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"STEER_TORQUE_CMD": torque, "STEER_REQUEST": steer_req} + return self.packer.make_can_msg_panda("STEERING_LKA", 0, values) + + def _angle_meas_msg(self, angle: float, steer_angle_initializing: bool = False): + # This creates a steering torque angle message. Not set on all platforms, + # relative to init angle on some older TSS2 platforms. Only to be used with LTA + values = {"STEER_ANGLE": angle, "STEER_ANGLE_INITIALIZING": int(steer_angle_initializing)} + return self.packer.make_can_msg_panda("STEER_TORQUE_SENSOR", 0, values) + + def _angle_cmd_msg(self, angle: float, enabled: bool): + return self._lta_msg(int(enabled), int(enabled), angle, torque_wind_down=100 if enabled else 0) + + def _lta_msg(self, req, req2, angle_cmd, torque_wind_down=100): + values = {"STEER_REQUEST": req, "STEER_REQUEST_2": req2, "STEER_ANGLE_CMD": angle_cmd, "TORQUE_WIND_DOWN": torque_wind_down} + return self.packer.make_can_msg_panda("STEERING_LTA", 0, values) + + def _accel_msg(self, accel, cancel_req=0): + values = {"ACCEL_CMD": accel, "CANCEL_REQ": cancel_req} + return self.packer.make_can_msg_panda("ACC_CONTROL", 0, values) + + def _speed_msg(self, speed): + values = {("WHEEL_SPEED_%s" % n): speed * 3.6 for n in ["FR", "FL", "RR", "RL"]} + return self.packer.make_can_msg_panda("WHEEL_SPEEDS", 0, values) + + def _user_brake_msg(self, brake): + values = {"BRAKE_PRESSED": brake} + return self.packer.make_can_msg_panda("BRAKE_MODULE", 0, values) + + def _user_gas_msg(self, gas): + cruise_active = self.safety.get_controls_allowed() + values = {"GAS_RELEASED": not gas, "CRUISE_ACTIVE": cruise_active} + return self.packer.make_can_msg_panda("PCM_CRUISE", 0, values) + + def _pcm_status_msg(self, enable): + values = {"CRUISE_ACTIVE": enable} + return self.packer.make_can_msg_panda("PCM_CRUISE", 0, values) + + def test_diagnostics(self, stock_longitudinal: bool = False): + for should_tx, msg in ((False, b"\x6D\x02\x3E\x00\x00\x00\x00\x00"), # fwdCamera tester present + (False, b"\x0F\x03\xAA\xAA\x00\x00\x00\x00"), # non-tester present + (True, b"\x0F\x02\x3E\x00\x00\x00\x00\x00")): + tester_present = libpanda_py.make_CANPacket(0x750, 0, msg) + self.assertEqual(should_tx and not stock_longitudinal, self._tx(tester_present)) + + def test_block_aeb(self, stock_longitudinal: bool = False): + for controls_allowed in (True, False): + for bad in (True, False): + for _ in range(10): + self.safety.set_controls_allowed(controls_allowed) + dat = [random.randint(1, 255) for _ in range(7)] + if not bad: + dat = [0]*6 + dat[-1:] + msg = libpanda_py.make_CANPacket(0x283, 0, bytes(dat)) + self.assertEqual(not bad and not stock_longitudinal, self._tx(msg)) + + # Only allow LTA msgs with no actuation + def test_lta_steer_cmd(self): + for engaged, req, req2, torque_wind_down, angle in itertools.product([True, False], + [0, 1], [0, 1], + [0, 50, 100], + np.linspace(-20, 20, 5)): + self.safety.set_controls_allowed(engaged) + + should_tx = not req and not req2 and angle == 0 and torque_wind_down == 0 + self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) + + def test_rx_hook(self): + # checksum checks + for msg in ["trq", "pcm"]: + self.safety.set_controls_allowed(1) + if msg == "trq": + to_push = self._torque_meas_msg(0) + if msg == "pcm": + to_push = self._pcm_status_msg(True) + self.assertTrue(self._rx(to_push)) + to_push[0].data[4] = 0 + to_push[0].data[5] = 0 + to_push[0].data[6] = 0 + to_push[0].data[7] = 0 + self.assertFalse(self._rx(to_push)) + self.assertFalse(self.safety.get_controls_allowed()) + + +class TestToyotaSafetyGasInterceptorBase(common.GasInterceptorSafetyTest, TestToyotaSafetyBase): + + TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS + GAS_INTERCEPTOR_TX_MSGS + INTERCEPTOR_THRESHOLD = 805 + + def setUp(self): + super().setUp() + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.safety.get_current_safety_param() | + Panda.FLAG_TOYOTA_GAS_INTERCEPTOR) + self.safety.init_tests() + + def test_stock_longitudinal(self): + # If stock longitudinal is set, the gas interceptor safety param should not be respected + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.safety.get_current_safety_param() | + Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL) + self.safety.init_tests() + + # Spot check a few gas interceptor tests: (1) reading interceptor, + # (2) behavior around interceptor, and (3) txing interceptor msgs + for test in (self.test_prev_gas_interceptor, self.test_disengage_on_gas_interceptor, + self.test_gas_interceptor_safety_check): + with self.subTest(test=test.__name__): + with self.assertRaises(AssertionError): + test() + + +class TestToyotaSafetyTorque(TestToyotaSafetyBase, common.MotorTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): + + MAX_RATE_UP = 15 + MAX_RATE_DOWN = 25 + MAX_TORQUE = 1500 + MAX_RT_DELTA = 450 + RT_INTERVAL = 250000 + MAX_TORQUE_ERROR = 350 + TORQUE_MEAS_TOLERANCE = 1 # toyota safety adds one to be conservative for rounding + + # Safety around steering req bit + MIN_VALID_STEERING_FRAMES = 18 + MAX_INVALID_STEERING_FRAMES = 1 + MIN_VALID_STEERING_RT_INTERVAL = 170000 # a ~10% buffer, can send steer up to 110Hz + + def setUp(self): + self.packer = CANPackerPanda("toyota_nodsu_pt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE) + self.safety.init_tests() + + +class TestToyotaSafetyTorqueGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaSafetyTorque): + pass + + +class TestToyotaSafetyAngle(TestToyotaSafetyBase, common.AngleSteeringSafetyTest): + + # Angle control limits + DEG_TO_CAN = 17.452007 # 1 / 0.0573 deg to can + + ANGLE_RATE_BP = [5., 25., 25.] + ANGLE_RATE_UP = [0.3, 0.15, 0.15] # windup limit + ANGLE_RATE_DOWN = [0.36, 0.26, 0.26] # unwind limit + + MAX_LTA_ANGLE = 94.9461 # PCS faults if commanding above this, deg + MAX_MEAS_TORQUE = 1500 # max allowed measured EPS torque before wind down + MAX_LTA_DRIVER_TORQUE = 150 # max allowed driver torque before wind down + + def setUp(self): + self.packer = CANPackerPanda("toyota_nodsu_pt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE | Panda.FLAG_TOYOTA_LTA) + self.safety.init_tests() + + # Only allow LKA msgs with no actuation + def test_lka_steer_cmd(self): + for engaged, steer_req, torque in itertools.product([True, False], + [0, 1], + np.linspace(-1500, 1500, 7)): + self.safety.set_controls_allowed(engaged) + torque = int(torque) + self.safety.set_rt_torque_last(torque) + self.safety.set_torque_meas(torque, torque) + self.safety.set_desired_torque_last(torque) + + should_tx = not steer_req and torque == 0 + self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(torque, steer_req))) + + def test_lta_steer_cmd(self): + """ + Tests the LTA steering command message + controls_allowed: + * STEER_REQUEST and STEER_REQUEST_2 do not mismatch + * TORQUE_WIND_DOWN is only set to 0 or 100 when STEER_REQUEST and STEER_REQUEST_2 are both 1 + * Full torque messages are blocked if either EPS torque or driver torque is above the threshold + + not controls_allowed: + * STEER_REQUEST, STEER_REQUEST_2, and TORQUE_WIND_DOWN are all 0 + """ + for controls_allowed in (True, False): + for angle in np.arange(-90, 90, 1): + self.safety.set_controls_allowed(controls_allowed) + self._reset_angle_measurement(angle) + self._set_prev_desired_angle(angle) + + self.assertTrue(self._tx(self._lta_msg(0, 0, angle, 0))) + if controls_allowed: + # Test the two steer request bits and TORQUE_WIND_DOWN torque wind down signal + for req, req2, torque_wind_down in itertools.product([0, 1], [0, 1], [0, 50, 100]): + mismatch = not (req or req2) and torque_wind_down != 0 + should_tx = req == req2 and (torque_wind_down in (0, 100)) and not mismatch + self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) + + # Test max EPS torque and driver override thresholds + cases = itertools.product( + (0, self.MAX_MEAS_TORQUE - 1, self.MAX_MEAS_TORQUE, self.MAX_MEAS_TORQUE + 1, self.MAX_MEAS_TORQUE * 2), + (0, self.MAX_LTA_DRIVER_TORQUE - 1, self.MAX_LTA_DRIVER_TORQUE, self.MAX_LTA_DRIVER_TORQUE + 1, self.MAX_LTA_DRIVER_TORQUE * 2) + ) + + for eps_torque, driver_torque in cases: + for sign in (-1, 1): + for _ in range(6): + self._rx(self._torque_meas_msg(sign * eps_torque, sign * driver_torque)) + + # Toyota adds 1 to EPS torque since it is rounded after EPS factor + should_tx = (eps_torque - 1) <= self.MAX_MEAS_TORQUE and driver_torque <= self.MAX_LTA_DRIVER_TORQUE + self.assertEqual(should_tx, self._tx(self._lta_msg(1, 1, angle, 100))) + self.assertTrue(self._tx(self._lta_msg(1, 1, angle, 0))) # should tx if we wind down torque + + else: + # Controls not allowed + for req, req2, torque_wind_down in itertools.product([0, 1], [0, 1], [0, 50, 100]): + should_tx = not (req or req2) and torque_wind_down == 0 + self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) + + def test_steering_angle_measurements(self, max_angle=None): + # Measurement test tests max angle + 0.5 which will fail + super().test_steering_angle_measurements(max_angle=self.MAX_LTA_ANGLE - 0.5) + + def test_angle_cmd_when_enabled(self, max_angle=None): + super().test_angle_cmd_when_enabled(max_angle=self.MAX_LTA_ANGLE) + + def test_angle_measurements(self): + """ + * Tests angle meas quality flag dictates whether angle measurement is parsed, and if rx is valid + * Tests rx hook correctly clips the angle measurement, since it is to be compared to LTA cmd when inactive + """ + for steer_angle_initializing in (True, False): + for angle in np.arange(0, self.MAX_LTA_ANGLE * 2, 1): + # If init flag is set, do not rx or parse any angle measurements + for a in (angle, -angle, 0, 0, 0, 0): + self.assertEqual(not steer_angle_initializing, + self._rx(self._angle_meas_msg(a, steer_angle_initializing))) + + final_angle = (0 if steer_angle_initializing else + round(min(angle, self.MAX_LTA_ANGLE) * self.DEG_TO_CAN)) + self.assertEqual(self.safety.get_angle_meas_min(), -final_angle) + self.assertEqual(self.safety.get_angle_meas_max(), final_angle) + + self._rx(self._angle_meas_msg(0)) + self.assertEqual(self.safety.get_angle_meas_min(), -final_angle) + self.assertEqual(self.safety.get_angle_meas_max(), 0) + + self._rx(self._angle_meas_msg(0)) + self.assertEqual(self.safety.get_angle_meas_min(), 0) + self.assertEqual(self.safety.get_angle_meas_max(), 0) + + +class TestToyotaSafetyAngleGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaSafetyAngle): + pass + + +class TestToyotaAltBrakeSafety(TestToyotaSafetyTorque): + + def setUp(self): + self.packer = CANPackerPanda("toyota_new_mc_pt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE | Panda.FLAG_TOYOTA_ALT_BRAKE) + self.safety.init_tests() + + def _user_brake_msg(self, brake): + values = {"BRAKE_PRESSED": brake} + return self.packer.make_can_msg_panda("BRAKE_MODULE", 0, values) + + # No LTA message in the DBC + def test_lta_steer_cmd(self): + pass + + +class TestToyotaAltBrakeSafetyGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaAltBrakeSafety): + pass + + +class TestToyotaStockLongitudinalBase(TestToyotaSafetyBase): + + TX_MSGS = TOYOTA_COMMON_TX_MSGS + # Base addresses minus ACC_CONTROL (0x343) + RELAY_MALFUNCTION_ADDRS = {0: (0x2E4,)} + FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191]} + + def test_diagnostics(self, stock_longitudinal: bool = True): + super().test_diagnostics(stock_longitudinal=stock_longitudinal) + + def test_block_aeb(self, stock_longitudinal: bool = True): + super().test_block_aeb(stock_longitudinal=stock_longitudinal) + + def test_accel_actuation_limits(self, stock_longitudinal=True): + super().test_accel_actuation_limits(stock_longitudinal=stock_longitudinal) + + def test_acc_cancel(self): + """ + Regardless of controls allowed, never allow ACC_CONTROL if cancel bit isn't set + """ + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + for accel in np.arange(self.MIN_ACCEL - 1, self.MAX_ACCEL + 1, 0.1): + self.assertFalse(self._tx(self._accel_msg(accel))) + should_tx = np.isclose(accel, 0, atol=0.0001) + self.assertEqual(should_tx, self._tx(self._accel_msg(accel, cancel_req=1))) + + +class TestToyotaStockLongitudinalTorque(TestToyotaStockLongitudinalBase, TestToyotaSafetyTorque): + + def setUp(self): + self.packer = CANPackerPanda("toyota_nodsu_pt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE | Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL) + self.safety.init_tests() + + +class TestToyotaStockLongitudinalAngle(TestToyotaStockLongitudinalBase, TestToyotaSafetyAngle): + + def setUp(self): + self.packer = CANPackerPanda("toyota_nodsu_pt_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE | Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL | Panda.FLAG_TOYOTA_LTA) + self.safety.init_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_volkswagen_mqb.py b/panda/tests/safety/test_volkswagen_mqb.py new file mode 100644 index 0000000..276ee6c --- /dev/null +++ b/panda/tests/safety/test_volkswagen_mqb.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +import unittest +import numpy as np +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + +MAX_ACCEL = 2.0 +MIN_ACCEL = -3.5 + +MSG_ESP_19 = 0xB2 # RX from ABS, for wheel speeds +MSG_LH_EPS_03 = 0x9F # RX from EPS, for driver steering torque +MSG_ESP_05 = 0x106 # RX from ABS, for brake light state +MSG_TSK_06 = 0x120 # RX from ECU, for ACC status from drivetrain coordinator +MSG_MOTOR_20 = 0x121 # RX from ECU, for driver throttle input +MSG_ACC_06 = 0x122 # TX by OP, ACC control instructions to the drivetrain coordinator +MSG_HCA_01 = 0x126 # TX by OP, Heading Control Assist steering torque +MSG_GRA_ACC_01 = 0x12B # TX by OP, ACC control buttons for cancel/resume +MSG_ACC_07 = 0x12E # TX by OP, ACC control instructions to the drivetrain coordinator +MSG_ACC_02 = 0x30C # TX by OP, ACC HUD data to the instrument cluster +MSG_LDW_02 = 0x397 # TX by OP, Lane line recognition and text alerts + + +class TestVolkswagenMqbSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS = {0: (MSG_HCA_01,)} + + MAX_RATE_UP = 4 + MAX_RATE_DOWN = 10 + MAX_TORQUE = 300 + MAX_RT_DELTA = 75 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 80 + DRIVER_TORQUE_FACTOR = 3 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestVolkswagenMqbSafety": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + # Wheel speeds _esp_19_msg + def _speed_msg(self, speed): + values = {"ESP_%s_Radgeschw_02" % s: speed for s in ["HL", "HR", "VL", "VR"]} + return self.packer.make_can_msg_panda("ESP_19", 0, values) + + # Driver brake pressure over threshold + def _esp_05_msg(self, brake): + values = {"ESP_Fahrer_bremst": brake} + return self.packer.make_can_msg_panda("ESP_05", 0, values) + + # Brake pedal switch + def _motor_14_msg(self, brake): + values = {"MO_Fahrer_bremst": brake} + return self.packer.make_can_msg_panda("Motor_14", 0, values) + + def _user_brake_msg(self, brake): + return self._motor_14_msg(brake) + + # Driver throttle input + def _user_gas_msg(self, gas): + values = {"MO_Fahrpedalrohwert_01": gas} + return self.packer.make_can_msg_panda("Motor_20", 0, values) + + # ACC engagement status + def _tsk_status_msg(self, enable, main_switch=True): + if main_switch: + tsk_status = 3 if enable else 2 + else: + tsk_status = 0 + values = {"TSK_Status": tsk_status} + return self.packer.make_can_msg_panda("TSK_06", 0, values) + + def _pcm_status_msg(self, enable): + return self._tsk_status_msg(enable) + + # Driver steering input torque + def _torque_driver_msg(self, torque): + values = {"EPS_Lenkmoment": abs(torque), "EPS_VZ_Lenkmoment": torque < 0} + return self.packer.make_can_msg_panda("LH_EPS_03", 0, values) + + # openpilot steering output torque + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"HCA_01_LM_Offset": abs(torque), "HCA_01_LM_OffSign": torque < 0, "HCA_01_Sendestatus": steer_req} + return self.packer.make_can_msg_panda("HCA_01", 0, values) + + # Cruise control buttons + def _gra_acc_01_msg(self, cancel=0, resume=0, _set=0, bus=2): + values = {"GRA_Abbrechen": cancel, "GRA_Tip_Setzen": _set, "GRA_Tip_Wiederaufnahme": resume} + return self.packer.make_can_msg_panda("GRA_ACC_01", bus, values) + + # Acceleration request to drivetrain coordinator + def _acc_06_msg(self, accel): + values = {"ACC_Sollbeschleunigung_02": accel} + return self.packer.make_can_msg_panda("ACC_06", 0, values) + + # Acceleration request to drivetrain coordinator + def _acc_07_msg(self, accel, secondary_accel=3.02): + values = {"ACC_Sollbeschleunigung_02": accel, "ACC_Folgebeschl": secondary_accel} + return self.packer.make_can_msg_panda("ACC_07", 0, values) + + # Verify brake_pressed is true if either the switch or pressure threshold signals are true + def test_redundant_brake_signals(self): + test_combinations = [(True, True, True), (True, True, False), (True, False, True), (False, False, False)] + for brake_pressed, motor_14_signal, esp_05_signal in test_combinations: + self._rx(self._motor_14_msg(False)) + self._rx(self._esp_05_msg(False)) + self.assertFalse(self.safety.get_brake_pressed_prev()) + self._rx(self._motor_14_msg(motor_14_signal)) + self._rx(self._esp_05_msg(esp_05_signal)) + self.assertEqual(brake_pressed, self.safety.get_brake_pressed_prev(), + f"expected {brake_pressed=} with {motor_14_signal=} and {esp_05_signal=}") + + def test_torque_measurements(self): + # TODO: make this test work with all cars + self._rx(self._torque_driver_msg(50)) + self._rx(self._torque_driver_msg(-50)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + + self.assertEqual(-50, self.safety.get_torque_driver_min()) + self.assertEqual(50, self.safety.get_torque_driver_max()) + + self._rx(self._torque_driver_msg(0)) + self.assertEqual(0, self.safety.get_torque_driver_max()) + self.assertEqual(-50, self.safety.get_torque_driver_min()) + + self._rx(self._torque_driver_msg(0)) + self.assertEqual(0, self.safety.get_torque_driver_max()) + self.assertEqual(0, self.safety.get_torque_driver_min()) + + +class TestVolkswagenMqbStockSafety(TestVolkswagenMqbSafety): + TX_MSGS = [[MSG_HCA_01, 0], [MSG_LDW_02, 0], [MSG_LH_EPS_03, 2], [MSG_GRA_ACC_01, 0], [MSG_GRA_ACC_01, 2]] + FWD_BLACKLISTED_ADDRS = {0: [MSG_LH_EPS_03], 2: [MSG_HCA_01, MSG_LDW_02]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + def setUp(self): + self.packer = CANPackerPanda("vw_mqb_2010") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN_MQB, 0) + self.safety.init_tests() + + def test_spam_cancel_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._gra_acc_01_msg(cancel=1))) + self.assertFalse(self._tx(self._gra_acc_01_msg(resume=1))) + self.assertFalse(self._tx(self._gra_acc_01_msg(_set=1))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._gra_acc_01_msg(resume=1))) + + +class TestVolkswagenMqbLongSafety(TestVolkswagenMqbSafety): + TX_MSGS = [[MSG_HCA_01, 0], [MSG_LDW_02, 0], [MSG_LH_EPS_03, 2], [MSG_ACC_02, 0], [MSG_ACC_06, 0], [MSG_ACC_07, 0]] + FWD_BLACKLISTED_ADDRS = {0: [MSG_LH_EPS_03], 2: [MSG_HCA_01, MSG_LDW_02, MSG_ACC_02, MSG_ACC_06, MSG_ACC_07]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + INACTIVE_ACCEL = 3.01 + + def setUp(self): + self.packer = CANPackerPanda("vw_mqb_2010") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN_MQB, Panda.FLAG_VOLKSWAGEN_LONG_CONTROL) + self.safety.init_tests() + + # stock cruise controls are entirely bypassed under openpilot longitudinal control + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def test_set_and_resume_buttons(self): + for button in ["set", "resume"]: + # ACC main switch must be on, engage on falling edge + self.safety.set_controls_allowed(0) + self._rx(self._tsk_status_msg(False, main_switch=False)) + self._rx(self._gra_acc_01_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} with main switch off") + self._rx(self._tsk_status_msg(False, main_switch=True)) + self._rx(self._gra_acc_01_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} rising edge") + self._rx(self._gra_acc_01_msg(bus=0)) + self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge") + + def test_cancel_button(self): + # Disable on rising edge of cancel button + self._rx(self._tsk_status_msg(False, main_switch=True)) + self.safety.set_controls_allowed(1) + self._rx(self._gra_acc_01_msg(cancel=True, bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after cancel") + + def test_main_switch(self): + # Disable as soon as main switch turns off + self._rx(self._tsk_status_msg(False, main_switch=True)) + self.safety.set_controls_allowed(1) + self._rx(self._tsk_status_msg(False, main_switch=False)) + self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after ACC main switch off") + + def test_accel_safety_check(self): + for controls_allowed in [True, False]: + # enforce we don't skip over 0 or inactive accel + for accel in np.concatenate((np.arange(MIN_ACCEL - 2, MAX_ACCEL + 2, 0.03), [0, self.INACTIVE_ACCEL])): + accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding + is_inactive_accel = accel == self.INACTIVE_ACCEL + send = (controls_allowed and MIN_ACCEL <= accel <= MAX_ACCEL) or is_inactive_accel + self.safety.set_controls_allowed(controls_allowed) + # primary accel request used by ECU + self.assertEqual(send, self._tx(self._acc_06_msg(accel)), (controls_allowed, accel)) + # additional accel request used by ABS/ESP + self.assertEqual(send, self._tx(self._acc_07_msg(accel)), (controls_allowed, accel)) + # ensure the optional secondary accel field remains inactive for now + self.assertEqual(is_inactive_accel, self._tx(self._acc_07_msg(accel, secondary_accel=accel)), (controls_allowed, accel)) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety/test_volkswagen_pq.py b/panda/tests/safety/test_volkswagen_pq.py new file mode 100644 index 0000000..f2bc317 --- /dev/null +++ b/panda/tests/safety/test_volkswagen_pq.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +import unittest +from panda import Panda +from panda.tests.libpanda import libpanda_py +import panda.tests.safety.common as common +from panda.tests.safety.common import CANPackerPanda + +MSG_LENKHILFE_3 = 0x0D0 # RX from EPS, for steering angle and driver steering torque +MSG_HCA_1 = 0x0D2 # TX by OP, Heading Control Assist steering torque +MSG_BREMSE_1 = 0x1A0 # RX from ABS, for ego speed +MSG_MOTOR_2 = 0x288 # RX from ECU, for CC state and brake switch state +MSG_ACC_SYSTEM = 0x368 # TX by OP, longitudinal acceleration controls +MSG_MOTOR_3 = 0x380 # RX from ECU, for driver throttle input +MSG_GRA_NEU = 0x38A # TX by OP, ACC control buttons for cancel/resume +MSG_MOTOR_5 = 0x480 # RX from ECU, for ACC main switch state +MSG_ACC_GRA_ANZEIGE = 0x56A # TX by OP, ACC HUD +MSG_LDW_1 = 0x5BE # TX by OP, Lane line recognition and text alerts + + +class TestVolkswagenPqSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): + cruise_engaged = False + + STANDSTILL_THRESHOLD = 0 + RELAY_MALFUNCTION_ADDRS = {0: (MSG_HCA_1,)} + + MAX_RATE_UP = 6 + MAX_RATE_DOWN = 10 + MAX_TORQUE = 300 + MAX_RT_DELTA = 113 + RT_INTERVAL = 250000 + + DRIVER_TORQUE_ALLOWANCE = 80 + DRIVER_TORQUE_FACTOR = 3 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "TestVolkswagenPqSafety": + cls.packer = None + cls.safety = None + raise unittest.SkipTest + + def _set_prev_torque(self, t): + self.safety.set_desired_torque_last(t) + self.safety.set_rt_torque_last(t) + + # Ego speed (Bremse_1) + def _speed_msg(self, speed): + values = {"Geschwindigkeit_neu__Bremse_1_": speed} + return self.packer.make_can_msg_panda("Bremse_1", 0, values) + + # Brake light switch (shared message Motor_2) + def _user_brake_msg(self, brake): + # since this signal is used for engagement status, preserve current state + return self._motor_2_msg(brake_pressed=brake, cruise_engaged=self.safety.get_controls_allowed()) + + # ACC engaged status (shared message Motor_2) + def _pcm_status_msg(self, enable): + self.__class__.cruise_engaged = enable + return self._motor_2_msg(cruise_engaged=enable) + + # Acceleration request to drivetrain coordinator + def _accel_msg(self, accel): + values = {"ACS_Sollbeschl": accel} + return self.packer.make_can_msg_panda("ACC_System", 0, values) + + # Driver steering input torque + def _torque_driver_msg(self, torque): + values = {"LH3_LM": abs(torque), "LH3_LMSign": torque < 0} + return self.packer.make_can_msg_panda("Lenkhilfe_3", 0, values) + + # openpilot steering output torque + def _torque_cmd_msg(self, torque, steer_req=1, hca_status=5): + values = {"LM_Offset": abs(torque), "LM_OffSign": torque < 0, "HCA_Status": hca_status if steer_req else 3} + return self.packer.make_can_msg_panda("HCA_1", 0, values) + + # ACC engagement and brake light switch status + # Called indirectly for compatibility with common.py tests + def _motor_2_msg(self, brake_pressed=False, cruise_engaged=False): + values = {"Bremslichtschalter": brake_pressed, + "GRA_Status": cruise_engaged} + return self.packer.make_can_msg_panda("Motor_2", 0, values) + + # ACC main switch status + def _motor_5_msg(self, main_switch=False): + values = {"GRA_Hauptschalter": main_switch} + return self.packer.make_can_msg_panda("Motor_5", 0, values) + + # Driver throttle input (Motor_3) + def _user_gas_msg(self, gas): + values = {"Fahrpedal_Rohsignal": gas} + return self.packer.make_can_msg_panda("Motor_3", 0, values) + + # Cruise control buttons (GRA_Neu) + def _button_msg(self, _set=False, resume=False, cancel=False, bus=2): + values = {"GRA_Neu_Setzen": _set, "GRA_Recall": resume, "GRA_Abbrechen": cancel} + return self.packer.make_can_msg_panda("GRA_Neu", bus, values) + + def test_torque_measurements(self): + # TODO: make this test work with all cars + self._rx(self._torque_driver_msg(50)) + self._rx(self._torque_driver_msg(-50)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + self._rx(self._torque_driver_msg(0)) + + self.assertEqual(-50, self.safety.get_torque_driver_min()) + self.assertEqual(50, self.safety.get_torque_driver_max()) + + self._rx(self._torque_driver_msg(0)) + self.assertEqual(0, self.safety.get_torque_driver_max()) + self.assertEqual(-50, self.safety.get_torque_driver_min()) + + self._rx(self._torque_driver_msg(0)) + self.assertEqual(0, self.safety.get_torque_driver_max()) + self.assertEqual(0, self.safety.get_torque_driver_min()) + + +class TestVolkswagenPqStockSafety(TestVolkswagenPqSafety): + # Transmit of GRA_Neu is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration + TX_MSGS = [[MSG_HCA_1, 0], [MSG_GRA_NEU, 0], [MSG_GRA_NEU, 2], [MSG_LDW_1, 0]] + FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + + def setUp(self): + self.packer = CANPackerPanda("vw_golf_mk4") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN_PQ, 0) + self.safety.init_tests() + + def test_spam_cancel_safety_check(self): + self.safety.set_controls_allowed(0) + self.assertTrue(self._tx(self._button_msg(cancel=True))) + self.assertFalse(self._tx(self._button_msg(resume=True))) + self.assertFalse(self._tx(self._button_msg(_set=True))) + # do not block resume if we are engaged already + self.safety.set_controls_allowed(1) + self.assertTrue(self._tx(self._button_msg(resume=True))) + + +class TestVolkswagenPqLongSafety(TestVolkswagenPqSafety, common.LongitudinalAccelSafetyTest): + TX_MSGS = [[MSG_HCA_1, 0], [MSG_LDW_1, 0], [MSG_ACC_SYSTEM, 0], [MSG_ACC_GRA_ANZEIGE, 0]] + FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1, MSG_ACC_SYSTEM, MSG_ACC_GRA_ANZEIGE]} + FWD_BUS_LOOKUP = {0: 2, 2: 0} + INACTIVE_ACCEL = 3.01 + + def setUp(self): + self.packer = CANPackerPanda("vw_golf_mk4") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_VOLKSWAGEN_PQ, Panda.FLAG_VOLKSWAGEN_LONG_CONTROL) + self.safety.init_tests() + + # stock cruise controls are entirely bypassed under openpilot longitudinal control + def test_disable_control_allowed_from_cruise(self): + pass + + def test_enable_control_allowed_from_cruise(self): + pass + + def test_cruise_engaged_prev(self): + pass + + def test_set_and_resume_buttons(self): + for button in ["set", "resume"]: + # ACC main switch must be on, engage on falling edge + self.safety.set_controls_allowed(0) + self._rx(self._motor_5_msg(main_switch=False)) + self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) + self._rx(self._button_msg(bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} with main switch off") + self._rx(self._motor_5_msg(main_switch=True)) + self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} rising edge") + self._rx(self._button_msg(bus=0)) + self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge") + + def test_cancel_button(self): + # Disable on rising edge of cancel button + self._rx(self._motor_5_msg(main_switch=True)) + self.safety.set_controls_allowed(1) + self._rx(self._button_msg(cancel=True, bus=0)) + self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after cancel") + + def test_main_switch(self): + # Disable as soon as main switch turns off + self._rx(self._motor_5_msg(main_switch=True)) + self.safety.set_controls_allowed(1) + self._rx(self._motor_5_msg(main_switch=False)) + self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after ACC main switch off") + + def test_torque_cmd_enable_variants(self): + # The EPS rack accepts either 5 or 7 for an enabled status, with different low speed tuning behavior + self.safety.set_controls_allowed(1) + for enabled_status in (5, 7): + self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP, steer_req=1, hca_status=enabled_status)), + f"torque cmd rejected with {enabled_status=}") + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/safety_replay/.gitignore b/panda/tests/safety_replay/.gitignore new file mode 100644 index 0000000..192fb09 --- /dev/null +++ b/panda/tests/safety_replay/.gitignore @@ -0,0 +1 @@ +*.bz2 diff --git a/panda/tests/safety_replay/__init__.py b/panda/tests/safety_replay/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/panda/tests/safety_replay/helpers.py b/panda/tests/safety_replay/helpers.py new file mode 100644 index 0000000..a1a6cd3 --- /dev/null +++ b/panda/tests/safety_replay/helpers.py @@ -0,0 +1,80 @@ +import panda.tests.libpanda.libpanda_py as libpanda_py +from panda import Panda + +def to_signed(d, bits): + ret = d + if d >= (1 << (bits - 1)): + ret = d - (1 << bits) + return ret + +def is_steering_msg(mode, param, addr): + ret = False + if mode in (Panda.SAFETY_HONDA_NIDEC, Panda.SAFETY_HONDA_BOSCH): + ret = (addr == 0xE4) or (addr == 0x194) or (addr == 0x33D) or (addr == 0x33DA) or (addr == 0x33DB) + elif mode == Panda.SAFETY_TOYOTA: + ret = addr == (0x191 if param & Panda.FLAG_TOYOTA_LTA else 0x2E4) + elif mode == Panda.SAFETY_GM: + ret = addr == 384 + elif mode == Panda.SAFETY_HYUNDAI: + ret = addr == 832 + elif mode == Panda.SAFETY_CHRYSLER: + ret = addr == 0x292 + elif mode == Panda.SAFETY_SUBARU: + ret = addr == 0x122 + elif mode == Panda.SAFETY_FORD: + ret = addr == 0x3d3 + elif mode == Panda.SAFETY_NISSAN: + ret = addr == 0x169 + return ret + +def get_steer_value(mode, param, to_send): + torque, angle = 0, 0 + if mode in (Panda.SAFETY_HONDA_NIDEC, Panda.SAFETY_HONDA_BOSCH): + torque = (to_send.data[0] << 8) | to_send.data[1] + torque = to_signed(torque, 16) + elif mode == Panda.SAFETY_TOYOTA: + if param & Panda.FLAG_TOYOTA_LTA: + angle = (to_send.data[1] << 8) | to_send.data[2] + angle = to_signed(angle, 16) + else: + torque = (to_send.data[1] << 8) | (to_send.data[2]) + torque = to_signed(torque, 16) + elif mode == Panda.SAFETY_GM: + torque = ((to_send.data[0] & 0x7) << 8) | to_send.data[1] + torque = to_signed(torque, 11) + elif mode == Panda.SAFETY_HYUNDAI: + torque = (((to_send.data[3] & 0x7) << 8) | to_send.data[2]) - 1024 + elif mode == Panda.SAFETY_CHRYSLER: + torque = (((to_send.data[0] & 0x7) << 8) | to_send.data[1]) - 1024 + elif mode == Panda.SAFETY_SUBARU: + torque = ((to_send.data[3] & 0x1F) << 8) | to_send.data[2] + torque = -to_signed(torque, 13) + elif mode == Panda.SAFETY_FORD: + angle = ((to_send.data[0] << 3) | (to_send.data[1] >> 5)) - 1000 + elif mode == Panda.SAFETY_NISSAN: + angle = (to_send.data[0] << 10) | (to_send.data[1] << 2) | (to_send.data[2] >> 6) + angle = -angle + (1310 * 100) + return torque, angle + +def package_can_msg(msg): + return libpanda_py.make_CANPacket(msg.address, msg.src % 4, msg.dat) + +def init_segment(safety, lr, mode, param): + sendcan = (msg for msg in lr if msg.which() == 'sendcan') + steering_msgs = (can for msg in sendcan for can in msg.sendcan if is_steering_msg(mode, param, can.address)) + + msg = next(steering_msgs, None) + if msg is None: + # no steering msgs + return + + to_send = package_can_msg(msg) + torque, angle = get_steer_value(mode, param, to_send) + if torque != 0: + safety.set_controls_allowed(1) + safety.set_desired_torque_last(torque) + elif angle != 0: + safety.set_controls_allowed(1) + safety.set_desired_angle_last(angle) + safety.set_angle_meas(angle, angle) + assert safety.safety_tx_hook(to_send), "failed to initialize panda safety for segment" diff --git a/panda/tests/safety_replay/replay_drive.py b/panda/tests/safety_replay/replay_drive.py new file mode 100644 index 0000000..df3e055 --- /dev/null +++ b/panda/tests/safety_replay/replay_drive.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +import argparse +import os +from collections import Counter + +from panda.tests.libpanda import libpanda_py +from panda.tests.safety_replay.helpers import package_can_msg, init_segment + +# replay a drive to check for safety violations +def replay_drive(lr, safety_mode, param, alternative_experience, segment=False): + safety = libpanda_py.libpanda + + err = safety.set_safety_hooks(safety_mode, param) + assert err == 0, "invalid safety mode: %d" % safety_mode + safety.set_alternative_experience(alternative_experience) + + if segment: + init_segment(safety, lr, safety_mode, param) + lr.reset() + + rx_tot, rx_invalid, tx_tot, tx_blocked, tx_controls, tx_controls_blocked = 0, 0, 0, 0, 0, 0 + safety_tick_rx_invalid = False + blocked_addrs = Counter() + invalid_addrs = set() + + can_msgs = [m for m in lr if m.which() in ('can', 'sendcan')] + start_t = can_msgs[0].logMonoTime + end_t = can_msgs[-1].logMonoTime + for msg in can_msgs: + safety.set_timer((msg.logMonoTime // 1000) % 0xFFFFFFFF) + + # skip start and end of route, warm up/down period + if msg.logMonoTime - start_t > 1e9 and end_t - msg.logMonoTime > 1e9: + safety.safety_tick_current_safety_config() + safety_tick_rx_invalid |= not safety.safety_config_valid() or safety_tick_rx_invalid + + if msg.which() == 'sendcan': + for canmsg in msg.sendcan: + to_send = package_can_msg(canmsg) + sent = safety.safety_tx_hook(to_send) + if not sent: + tx_blocked += 1 + tx_controls_blocked += safety.get_controls_allowed() + blocked_addrs[canmsg.address] += 1 + + if "DEBUG" in os.environ: + print("blocked bus %d msg %d at %f" % (canmsg.src, canmsg.address, (msg.logMonoTime - start_t) / 1e9)) + tx_controls += safety.get_controls_allowed() + tx_tot += 1 + elif msg.which() == 'can': + # ignore msgs we sent + for canmsg in filter(lambda m: m.src < 128, msg.can): + to_push = package_can_msg(canmsg) + recv = safety.safety_rx_hook(to_push) + if not recv: + rx_invalid += 1 + invalid_addrs.add(canmsg.address) + rx_tot += 1 + + print("\nRX") + print("total rx msgs:", rx_tot) + print("invalid rx msgs:", rx_invalid) + print("safety tick rx invalid:", safety_tick_rx_invalid) + print("invalid addrs:", invalid_addrs) + print("\nTX") + print("total openpilot msgs:", tx_tot) + print("total msgs with controls allowed:", tx_controls) + print("blocked msgs:", tx_blocked) + print("blocked with controls allowed:", tx_controls_blocked) + print("blocked addrs:", blocked_addrs) + + return tx_controls_blocked == 0 and rx_invalid == 0 and not safety_tick_rx_invalid + +if __name__ == "__main__": + from openpilot.tools.lib.logreader import LogReader + + parser = argparse.ArgumentParser(description="Replay CAN messages from a route or segment through a safety mode", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("route_or_segment_name", nargs='+') + parser.add_argument("--mode", type=int, help="Override the safety mode from the log") + parser.add_argument("--param", type=int, help="Override the safety param from the log") + parser.add_argument("--alternative-experience", type=int, help="Override the alternative experience from the log") + args = parser.parse_args() + + lr = LogReader(args.route_or_segment_name[0]) + + if None in (args.mode, args.param, args.alternative_experience): + for msg in lr: + if msg.which() == 'carParams': + if args.mode is None: + args.mode = msg.carParams.safetyConfigs[-1].safetyModel.raw + if args.param is None: + args.param = msg.carParams.safetyConfigs[-1].safetyParam + if args.alternative_experience is None: + args.alternative_experience = msg.carParams.alternativeExperience + break + else: + raise Exception("carParams not found in log. Set safety mode and param manually.") + + lr.reset() + + print(f"replaying {args.route_or_segment_name[0]} with safety mode {args.mode}, param {args.param}, alternative experience {args.alternative_experience}") + replay_drive(lr, args.mode, args.param, args.alternative_experience, segment=len(lr.logreader_identifiers) == 1) diff --git a/panda/tests/setup_device_ci.sh b/panda/tests/setup_device_ci.sh new file mode 100644 index 0000000..b45c6b4 --- /dev/null +++ b/panda/tests/setup_device_ci.sh @@ -0,0 +1,74 @@ +#!/usr/bin/bash + +set -e + +if [ -z "$SOURCE_DIR" ]; then + echo "SOURCE_DIR must be set" + exit 1 +fi + +if [ -z "$GIT_COMMIT" ]; then + echo "GIT_COMMIT must be set" + exit 1 +fi + +if [ -z "$TEST_DIR" ]; then + echo "TEST_DIR must be set" + exit 1 +fi + +CONTINUE_PATH="/data/continue.sh" +tee $CONTINUE_PATH << EOF +#!/usr/bin/bash + +sudo abctl --set_success + +# patch sshd config +sudo mount -o rw,remount / +sudo sed -i "s,/data/params/d/GithubSshKeys,/usr/comma/setup_keys," /etc/ssh/sshd_config +sudo systemctl daemon-reload +sudo systemctl restart ssh +sudo systemctl disable ssh-param-watcher.path +sudo systemctl disable ssh-param-watcher.service +sudo mount -o ro,remount / + +while true; do + if ! sudo systemctl is-active -q ssh; then + sudo systemctl start ssh + fi + sleep 5s +done + +sleep infinity +EOF +chmod +x $CONTINUE_PATH + + +# set up environment +if [ ! -d "$SOURCE_DIR" ]; then + git clone https://github.com/commaai/panda.git $SOURCE_DIR +fi + +# setup device/SOM state +SOM_ST_IO=49 +echo $SOM_ST_IO > /sys/class/gpio/export || true +echo out > /sys/class/gpio/gpio${SOM_ST_IO}/direction +echo 1 > /sys/class/gpio/gpio${SOM_ST_IO}/value + +# checkout panda commit +cd $SOURCE_DIR + +rm -f .git/index.lock +git reset --hard +git fetch --no-tags --no-recurse-submodules -j4 --verbose --depth 1 origin $GIT_COMMIT +find . -maxdepth 1 -not -path './.git' -not -name '.' -not -name '..' -exec rm -rf '{}' \; +git reset --hard $GIT_COMMIT +git checkout $GIT_COMMIT +git clean -xdff + +echo "git checkout done, t=$SECONDS" +du -hs $SOURCE_DIR $SOURCE_DIR/.git + +rsync -a --delete $SOURCE_DIR $TEST_DIR + +echo "$TEST_DIR synced with $GIT_COMMIT, t=$SECONDS" diff --git a/panda/tests/som/on-device.py b/panda/tests/som/on-device.py new file mode 100644 index 0000000..f88d5a9 --- /dev/null +++ b/panda/tests/som/on-device.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import os +import time + +from panda import Panda + + +if __name__ == "__main__": + flag_set = False + while True: + try: + with Panda(disable_checks=False) as p: + if not flag_set: + p.set_heartbeat_disabled() + p.set_safety_mode(Panda.SAFETY_ELM327, 30) + flag_set = True + + # shutdown when told + dt = p.get_datetime() + if dt.year == 2040 and dt.month == 8: + os.system("sudo poweroff") + except Exception as e: + print(str(e)) + time.sleep(0.5) diff --git a/panda/tests/som/test_bootkick.py b/panda/tests/som/test_bootkick.py new file mode 100644 index 0000000..6c08e1a --- /dev/null +++ b/panda/tests/som/test_bootkick.py @@ -0,0 +1,154 @@ +import time +import pytest +import datetime + +from panda import Panda, PandaJungle + +PANDA_SERIAL = "28002d000451323431333839" +JUNGLE_SERIAL = "26001c001451313236343430" + +OBDC_PORT = 1 + +@pytest.fixture(autouse=True, scope="function") +def pj(): + jungle = PandaJungle(JUNGLE_SERIAL) + jungle.flash() + + jungle.reset() + jungle.set_ignition(False) + + yield jungle + + jungle.set_panda_power(False) + jungle.close() + +@pytest.fixture(scope="function") +def p(pj): + # note that the 3X's panda lib isn't updated, which + # shold be fine since it only uses stable APIs + pj.set_panda_power(True) + assert Panda.wait_for_panda(PANDA_SERIAL, 10) + p = Panda(PANDA_SERIAL) + p.flash() + p.reset() + yield p + p.close() + +def setup_state(panda, jungle, state): + jungle.set_panda_power(0) + + if state == "off": + wait_for_full_poweroff(jungle) + elif state == "normal boot": + jungle.set_panda_individual_power(OBDC_PORT, 1) + elif state == "QDL": + time.sleep(0.5) + jungle.set_panda_individual_power(OBDC_PORT, 1) + elif state == "ready to bootkick": + wait_for_full_poweroff(jungle) + jungle.set_panda_individual_power(OBDC_PORT, 1) + wait_for_boot(panda, jungle) + set_som_shutdown_flag(panda) + panda.set_safety_mode(Panda.SAFETY_SILENT) + panda.send_heartbeat() + wait_for_som_shutdown(panda, jungle) + else: + raise ValueError(f"unkown state: {state}") + + +def wait_for_som_shutdown(panda, jungle): + st = time.monotonic() + while panda.read_som_gpio(): + # can take a while for the SOM to fully shutdown + if time.monotonic() - st > 120: + raise Exception("SOM didn't shutdown in time") + if check_som_boot_flag(panda): + raise Exception(f"SOM rebooted instead of shutdown: {time.monotonic() - st}s") + time.sleep(0.5) + dt = time.monotonic() - st + print("waiting for shutdown", round(dt)) + dt = time.monotonic() - st + print(f"took {dt:.2f}s for SOM to shutdown") + +def wait_for_full_poweroff(jungle, timeout=30): + st = time.monotonic() + + time.sleep(15) + while PANDA_SERIAL in Panda.list(): + if time.monotonic() - st > timeout: + raise Exception("took too long for device to turn off") + + health = jungle.health() + assert all(health[f"ch{i}_power"] < 0.1 for i in range(1, 7)) + +def check_som_boot_flag(panda): + h = panda.health() + return h['safety_mode'] == Panda.SAFETY_ELM327 and h['safety_param'] == 30 + +def set_som_shutdown_flag(panda): + panda.set_datetime(datetime.datetime(year=2040, month=8, day=23)) + +def wait_for_boot(panda, jungle, reset_expected=False, bootkick=False, timeout=120): + st = time.monotonic() + + Panda.wait_for_panda(PANDA_SERIAL, timeout) + panda.reconnect() + if bootkick: + assert panda.health()['uptime'] > 20 + else: + assert panda.health()['uptime'] < 3 + + for i in range(3): + assert not check_som_boot_flag(panda) + time.sleep(1) + + # wait for SOM to bootup + while not check_som_boot_flag(panda): + if time.monotonic() - st > timeout: + raise Exception("SOM didn't boot in time") + time.sleep(1.0) + + assert panda.health()['som_reset_triggered'] == reset_expected + +def test_cold_boot(p, pj): + setup_state(p, pj, "off") + setup_state(p, pj, "normal boot") + wait_for_boot(p, pj) + +def test_bootkick_ignition_line(p, pj): + setup_state(p, pj, "ready to bootkick") + pj.set_ignition(True) + wait_for_boot(p, pj, bootkick=True) + +@pytest.mark.skip("test isn't reliable yet") +def test_bootkick_can_ignition(p, pj): + setup_state(p, pj, "ready to bootkick") + for _ in range(10): + # Mazda ignition signal + pj.can_send(0x9E, b'\xc0\x00\x00\x00\x00\x00\x00\x00', 0) + time.sleep(0.5) + wait_for_boot(p, pj, bootkick=True) + +def test_recovery_from_qdl(p, pj): + setup_state(p, pj, "ready to bootkick") + + # put into QDL using the FORCE_USB_BOOT pin + for i in range(10): + pj.set_header_pin(i, 1) + + # try to boot + time.sleep(1) + pj.set_ignition(True) + time.sleep(3) + + # release FORCE_USB_BOOT + for i in range(10): + pj.set_header_pin(i, 0) + + # normally, this GPIO is set immediately since it's first enabled in the ABL + for i in range(40): + assert not p.read_som_gpio() + time.sleep(1) + + # should boot after 45s + wait_for_boot(p, pj, reset_expected=True, bootkick=True, timeout=120) diff --git a/panda/tests/som_debug.sh b/panda/tests/som_debug.sh new file mode 100644 index 0000000..9bb4219 --- /dev/null +++ b/panda/tests/som_debug.sh @@ -0,0 +1,7 @@ +#!/usr/bin/bash +set -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +PYTHONUNBUFFERED=1 NO_COLOR=1 CLAIM=1 PORT=4 ./debug_console.py diff --git a/panda/tests/spam_can.py b/panda/tests/spam_can.py new file mode 100644 index 0000000..3154cc0 --- /dev/null +++ b/panda/tests/spam_can.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import os +import random + +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +if __name__ == "__main__": + p = Panda() + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + print("Spamming all buses...") + while True: + at = random.randint(1, 2000) + st = get_test_string()[0:8] + bus = random.randint(0, 2) + p.can_send(at, st, bus) + # print("Sent message on bus: ", bus) diff --git a/panda/tests/standalone_test.py b/panda/tests/standalone_test.py new file mode 100644 index 0000000..7ec5559 --- /dev/null +++ b/panda/tests/standalone_test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +import struct +import time + +from panda import Panda + +if __name__ == "__main__": + p = Panda() + print(p.get_serial()) + print(p.health()) + + t1 = time.time() + for _ in range(100): + p.get_serial() + t2 = time.time() + print("100 requests took %.2f ms" % ((t2 - t1) * 1000)) + + p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + a = 0 + while True: + # flood + msg = b"\xaa" * 4 + struct.pack("I", a) + p.can_send(0xaa, msg, 0) + p.can_send(0xaa, msg, 1) + p.can_send(0xaa, msg, 4) + time.sleep(0.01) + + dat = p.can_recv() + if len(dat) > 0: + print(dat) + a += 1 diff --git a/panda/tests/test_rsa.c b/panda/tests/test_rsa.c new file mode 100644 index 0000000..5c784e2 --- /dev/null +++ b/panda/tests/test_rsa.c @@ -0,0 +1,34 @@ +/* +gcc -DTEST_RSA test_rsa.c ../crypto/rsa.c ../crypto/sha.c && ./a.out +*/ + +#include +#include + +#define MAX_LEN 0x40000 +char buf[MAX_LEN]; + +#include "../crypto/sha.h" +#include "../crypto/rsa.h" +#include "../obj/cert.h" + +int main() { + FILE *f = fopen("../obj/panda.bin", "rb"); + int tlen = fread(buf, 1, MAX_LEN, f); + fclose(f); + printf("read %d\n", tlen); + uint32_t *_app_start = (uint32_t *)buf; + + int len = _app_start[0]; + char digest[SHA_DIGEST_SIZE]; + SHA_hash(&_app_start[1], len-4, digest); + printf("SHA hash done\n"); + + if (!RSA_verify(&rsa_key, ((void*)&_app_start[0]) + len, RSANUMBYTES, digest, SHA_DIGEST_SIZE)) { + printf("RSA fail\n"); + } else { + printf("RSA match!!!\n"); + } + + return 0; +} diff --git a/panda/tests/tucan_loopback.py b/panda/tests/tucan_loopback.py new file mode 100644 index 0000000..457facd --- /dev/null +++ b/panda/tests/tucan_loopback.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import os +import time +import random +import argparse +from itertools import permutations + +from panda import Panda + +def get_test_string(): + return b"test" + os.urandom(10) + +def run_test(sleep_duration): + pandas = Panda.list() + print(pandas) + + if len(pandas) < 2: + raise Exception("Two pandas are needed for test") + + run_test_w_pandas(pandas, sleep_duration) + +def run_test_w_pandas(pandas, sleep_duration): + h = [Panda(x) for x in pandas] + print("H", h) + + for hh in h: + hh.set_safety_mode(Panda.SAFETY_ALLOUTPUT) + + # test both directions + for ho in permutations(list(range(len(h))), r=2): + print("***************** TESTING", ho) + + panda0, panda1 = h[ho[0]], h[ho[1]] + + # **** test health packet **** + print("health", ho[0], h[ho[0]].health()) + + # **** test can line loopback **** + # for bus, gmlan in [(0, None), (1, False), (2, False), (1, True), (2, True)]: + for bus, gmlan in [(0, None), (1, None)]: + print("\ntest can", bus) + # flush + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + if gmlan is not None: + panda0.set_gmlan(gmlan, bus) + panda1.set_gmlan(gmlan, bus) + + # send the characters + # pick addresses high enough to not conflict with honda code + at = random.randint(1024, 2000) + st = get_test_string()[0:8] + panda0.can_send(at, st, bus) + time.sleep(0.1) + + # check for receive + cans_echo = panda0.can_recv() + cans_loop = panda1.can_recv() + + print("Bus", bus, "echo", cans_echo, "loop", cans_loop) + + assert len(cans_echo) == 1 + assert len(cans_loop) == 1 + + assert cans_echo[0][0] == at + assert cans_loop[0][0] == at + + assert cans_echo[0][2] == st + assert cans_loop[0][2] == st + + assert cans_echo[0][3] == 0x80 | bus + if cans_loop[0][3] != bus: + print("EXPECTED %d GOT %d" % (bus, cans_loop[0][3])) + assert cans_loop[0][3] == bus + + print("CAN pass", bus, ho) + time.sleep(sleep_duration) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-n", type=int, help="Number of test iterations to run") + parser.add_argument("-sleep", type=int, help="Sleep time between tests", default=0) + args = parser.parse_args() + + if args.n is None: + while True: + run_test(sleep_duration=args.sleep) + else: + for _ in range(args.n): + run_test(sleep_duration=args.sleep) diff --git a/panda/tests/usbprotocol/test.sh b/panda/tests/usbprotocol/test.sh new file mode 100644 index 0000000..8e3886d --- /dev/null +++ b/panda/tests/usbprotocol/test.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +# Loops over all HW_TYPEs, see board/boards/board_declarations.h +for hw_type in {0..7}; do + echo "Testing HW_TYPE: $hw_type" + HW_TYPE=$hw_type python -m unittest discover . +done diff --git a/panda/tests/usbprotocol/test_comms.py b/panda/tests/usbprotocol/test_comms.py new file mode 100644 index 0000000..c08551b --- /dev/null +++ b/panda/tests/usbprotocol/test_comms.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +import random +import unittest + +from panda import Panda, DLC_TO_LEN, USBPACKET_MAX_SIZE, pack_can_buffer, unpack_can_buffer +from panda.tests.libpanda import libpanda_py + +lpp = libpanda_py.libpanda + +CHUNK_SIZE = USBPACKET_MAX_SIZE +TX_QUEUES = (lpp.tx1_q, lpp.tx2_q, lpp.tx3_q, lpp.txgmlan_q) + + +def unpackage_can_msg(pkt): + dat_len = DLC_TO_LEN[pkt[0].data_len_code] + dat = bytes(pkt[0].data[0:dat_len]) + return pkt[0].addr, 0, dat, pkt[0].bus + + +def random_can_messages(n, bus=None): + msgs = [] + for _ in range(n): + if bus is None: + bus = random.randint(0, 3) + address = random.randint(1, (1 << 29) - 1) + data = bytes([random.getrandbits(8) for _ in range(DLC_TO_LEN[random.randrange(0, len(DLC_TO_LEN))])]) + msgs.append((address, 0, data, bus)) + return msgs + + +class TestPandaComms(unittest.TestCase): + def setUp(self): + lpp.comms_can_reset() + + def test_tx_queues(self): + for bus in range(4): + message = (0x100, 0, b"test", bus) + + can_pkt_tx = libpanda_py.make_CANPacket(message[0], message[3], message[2]) + can_pkt_rx = libpanda_py.ffi.new('CANPacket_t *') + + assert lpp.can_push(TX_QUEUES[bus], can_pkt_tx), "CAN push failed" + assert lpp.can_pop(TX_QUEUES[bus], can_pkt_rx), "CAN pop failed" + + assert unpackage_can_msg(can_pkt_rx) == message + + def test_comms_reset_rx(self): + # store some test messages in the queue + test_msg = (0x100, 0, b"test", 0) + for _ in range(100): + can_pkt_tx = libpanda_py.make_CANPacket(test_msg[0], test_msg[3], test_msg[2]) + lpp.can_push(lpp.rx_q, can_pkt_tx) + + # read a small chunk such that we have some overflow + TINY_CHUNK_SIZE = 6 + dat = libpanda_py.ffi.new(f"uint8_t[{TINY_CHUNK_SIZE}]") + rx_len = lpp.comms_can_read(dat, TINY_CHUNK_SIZE) + assert rx_len == TINY_CHUNK_SIZE, "comms_can_read returned too little data" + + _, overflow = unpack_can_buffer(bytes(dat)) + assert len(overflow) > 0, "overflow buffer should not be empty" + + # reset the comms to clear the overflow buffer on the panda side + lpp.comms_can_reset() + + # read a large chunk, which should now contain valid messages + LARGE_CHUNK_SIZE = 512 + dat = libpanda_py.ffi.new(f"uint8_t[{LARGE_CHUNK_SIZE}]") + rx_len = lpp.comms_can_read(dat, LARGE_CHUNK_SIZE) + assert rx_len == LARGE_CHUNK_SIZE, "comms_can_read returned too little data" + + msgs, _ = unpack_can_buffer(bytes(dat)) + assert len(msgs) > 0, "message buffer should not be empty" + for m in msgs: + assert m == test_msg, "message buffer should contain valid test messages" + + def test_comms_reset_tx(self): + # store some test messages in the queue + test_msg = (0x100, 0, b"test", 0) + packed = pack_can_buffer([test_msg for _ in range(100)]) + + # write a small chunk such that we have some overflow + TINY_CHUNK_SIZE = 6 + lpp.comms_can_write(packed[0][:TINY_CHUNK_SIZE], TINY_CHUNK_SIZE) + + # reset the comms to clear the overflow buffer on the panda side + lpp.comms_can_reset() + + # write a full valid chunk, which should now contain valid messages + lpp.comms_can_write(packed[1], len(packed[1])) + + # read the messages from the queue and make sure they're valid + queue_msgs = [] + pkt = libpanda_py.ffi.new('CANPacket_t *') + while lpp.can_pop(TX_QUEUES[0], pkt): + queue_msgs.append(unpackage_can_msg(pkt)) + + assert len(queue_msgs) > 0, "message buffer should not be empty" + for m in queue_msgs: + assert m == test_msg, "message buffer should contain valid test messages" + + + def test_can_send_usb(self): + lpp.set_safety_hooks(Panda.SAFETY_ALLOUTPUT, 0) + + for bus in range(3): + with self.subTest(bus=bus): + for _ in range(100): + msgs = random_can_messages(200, bus=bus) + packed = pack_can_buffer(msgs) + + # Simulate USB bulk chunks + for buf in packed: + for i in range(0, len(buf), CHUNK_SIZE): + chunk_len = min(CHUNK_SIZE, len(buf) - i) + lpp.comms_can_write(buf[i:i+chunk_len], chunk_len) + + # Check that they ended up in the right buffers + queue_msgs = [] + pkt = libpanda_py.ffi.new('CANPacket_t *') + while lpp.can_pop(TX_QUEUES[bus], pkt): + queue_msgs.append(unpackage_can_msg(pkt)) + + self.assertEqual(len(queue_msgs), len(msgs)) + self.assertEqual(queue_msgs, msgs) + + def test_can_receive_usb(self): + msgs = random_can_messages(50000) + packets = [libpanda_py.make_CANPacket(m[0], m[3], m[2]) for m in msgs] + + rx_msgs = [] + overflow_buf = b"" + while len(packets) > 0: + # Push into queue + while lpp.can_slots_empty(lpp.rx_q) > 0 and len(packets) > 0: + lpp.can_push(lpp.rx_q, packets.pop(0)) + + # Simulate USB bulk IN chunks + MAX_TRANSFER_SIZE = 16384 + dat = libpanda_py.ffi.new(f"uint8_t[{CHUNK_SIZE}]") + while True: + buf = b"" + while len(buf) < MAX_TRANSFER_SIZE: + max_size = min(CHUNK_SIZE, MAX_TRANSFER_SIZE - len(buf)) + rx_len = lpp.comms_can_read(dat, max_size) + buf += bytes(dat[0:rx_len]) + if rx_len < max_size: + break + + if len(buf) == 0: + break + unpacked_msgs, overflow_buf = unpack_can_buffer(overflow_buf + buf) + rx_msgs.extend(unpacked_msgs) + + self.assertEqual(len(rx_msgs), len(msgs)) + self.assertEqual(rx_msgs, msgs) + + +if __name__ == "__main__": + unittest.main() diff --git a/panda/tests/usbprotocol/test_pandalib.py b/panda/tests/usbprotocol/test_pandalib.py new file mode 100644 index 0000000..c03f246 --- /dev/null +++ b/panda/tests/usbprotocol/test_pandalib.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +import random +import unittest + +from panda import pack_can_buffer, unpack_can_buffer, DLC_TO_LEN + +class PandaTestPackUnpack(unittest.TestCase): + def test_panda_lib_pack_unpack(self): + overflow_buf = b'' + + to_pack = [] + for _ in range(10000): + address = random.randint(1, (1 << 29) - 1) + data = bytes([random.getrandbits(8) for _ in range(DLC_TO_LEN[random.randrange(0, len(DLC_TO_LEN))])]) + to_pack.append((address, 0, data, 0)) + + packed = pack_can_buffer(to_pack) + unpacked = [] + for dat in packed: + msgs, overflow_buf = unpack_can_buffer(overflow_buf + dat) + unpacked.extend(msgs) + + self.assertEqual(unpacked, to_pack) + +if __name__ == "__main__": + unittest.main()

{cGfAjI(^Kg96&k+V-0R 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%0Ai+@> zL`dtEP$0S*T-ne%*B3&jPFI^F&|;lU&u1A;_g&I_GqiYPgc)9)0OYw-2w7T>!MI|k z%Y`r^f5fvSa-Kn#7=eK|twS(zX;LY^9mF?-&{6+9Y#Hg9y;CW^d&KvSz-<=OGkY^1 zYr&g?fdI5>;taRUTu#Z85ki($Ka7l-bTEL+w&QjG+2+3L3YivPpfV!?c_NN%#1U!* z8W*{~Eb-)My&%p3T6`~y=|XAuZ)n9dMR_GlQVqArQgXj231+jxdd70i_J1D?%7qr& zS-fH>V#JiSOws%iXUZv4DhT)Js8wh*92d#*n4}vkj6Qf>CEgiY?7P#&iI}pMDK1XL zl(S6H;wK#_iyfz#6JBBY1{jnHtwk_#AvpH(CAT>+3`~12Oswy$#PIhv7&E0YZoX{z z7q64ZY4IJvc;rEf?;Y};LsNf2iZ2)P#lnQ9b_~(|42ELRejdh%fntYJ!51R$_Dr;o z2-5(qufe487HHVxC?oG@yj^%Qv=}@gBe{g_Het)sav^dyM7&D&fN7ubXJ|2%>Cr|^ zS<4iUiEnrL`W0>1FG5UE>I}7~BzjK(QG5G-1loa%qI^ z*>Up9MVCT`apLpOkR!uO7q4vTAIqwdk*ABaO48iltyS*K&J1GPFG^Bve(r^6K?-ZY@&Y#1~kTIa&xXm&Ez zY4qK7jNp8Qow;Tf5|xH5knvkEC{J476K5|i8pL~JoeoA|8f;@2jxCzys?Rf!=7%C& zhSo6{CLHh5oH;%w6GDa-FZY|7>7X!WX(>~DG-XVOg(*i%nWB6KsSm;bL1Br#pW*~& zfNu#gL+cOX?5Fh#j1x*+qWJbV!s69_)1{4~dq(2O(sI!a*yytLD7pa~T|8fmji6hWz4GDC)w88-q=kf4l#PP=W5Q||@~=eMw78KM7qCHYbG!!r@qvYb@qQ@I zU`~^Bm<%oc^v4CKYl$P!;%|ZC@w)JMe;a3>f8owOX?2UUpB8UNbYbY)=?EH(HiKRw z-dS4YAb(UexbMgN8-2_v-2B0h3A-K;TM$t~;c7o67c`cI9)?RMImaP z7Jn>+$n`fv@a0d3u6Dk(_+rxZ(&2B7rU#8uaTgTsUM%U*x&bC04ZdH0Nz3A;7hZDl zdA#o8yluQhm*HnYJxQ;%>;y-#;yaTX7pOWl_wH;&}$^72!(14)kV2-w)hk@PokY2+FUw zdnlFX>%FCys)guOMU+!+^Z99 zNaN!A>U5#|vV>dHy$v**?|Xo4IdMVHbd;YfdM=U`e+`&#YbxfFf(tmsTvN;kX5CQM z7l04Iy}Ehv&2y%=f-oF+DQr62#A!k}%@g;Ea=;zU^Y%9GOW(p?6Q&6oWmaBpg)vRb ztMYJ7veMCRZgef}iaAd``K2%VGFLw8_g(Qgw#<3z%TInOQ8!J{W0rtVWmyKyaEhsg zvOKizsQS6|oAx3P{d1mjF|yCFyr@2HhC9)xw$6F#D@fc)sWMWpStY#lG%t`*xuP_w z@Ej99N%NhOCI$wTkqQiD)bgVS!}ep#PifpaJ=4)N%`k6=Kx0`XWDn!zCh%d1Z5vn5X`Uy2i1{K9x6scP z9w~z`-X-u~<@|3(eO&PridfSugVq9CS923-ms;9^8E9I^4}-=u(U)1r4R^DS@o0I! za8kU@w497{6!0s3@1J6;!SEjhwteI71im+cKb*i1Ch#K({AdFIeF8s`z+X<_ZzS+P zC-Bn={KEu(Hi3VZz`si1-zV@Z3EVJ0US4S2vGQw9;Q0xBS^~Eu@YxBxB!Sx#ctrxg zErGid__74fB=A52Uzxyz2|Sv>;|W|#;Oi22PXgbPzy}ie&IG}4}0zaR?ze(Vi68O~wZp0a&?cZ|}cwPdZn!pPa z_{;=eoWQLKyexq)N#Io;>sZS3%vOv~etZkZEuLlCF?=tuox=|R*CgEZIhoYyoB z9*%K5?Q~;rn8qsj+j)Tpz?^R}SB3MDj-xycwmRJ134Biie+c+0#HHi-OThfelaAwu z6XSRp%US9qDl=F1d@ojK3X>0mV0AGpp5l-fj>l5iO z#G2EV_Yz>vT^b*ku5AW$?P}?KA%$tFoDTvobNS_b7tVvv;9Z$>!Pn6|U7zH8JDl7! z4!-5IUU=O+tFrKYBc`po3tyBvBz)nVUB#Te+NoHpU#H=?eqISa#at&ZG4b$?%e}yf z^-uMwBh;6oxt#A`W{|hFMt`I{oTt0-ZYkwLy)oaHah}&YD1){Kv`depYx5P=-4Zrx ze;O~}SEXLkNu4D-2hv3O;`^}FqcokPz!~7KL^?|6Qr@+p_*zTLg=_mV!Yh6jm~O>< z1)Vyk))T+o(G9F^Rr#)le~aPU1I%#Br+Ir2{s+Z>fzzUE&2v>&uI`sbVKS<3DxItO zK51w1ytE(;>w~}Xc^n2-1a}O}{n`kF} zbF`sAThzA6w>kMsNvv7+p0IEBEwgTI+@F3oT1IV7^FpIH^Tkm1KTV?>_=peJwC{vl zX5Tt%f8)E;w^0ZD_?+ezuEn+d_*Pj9!rF05>!uB~QK3nD4D)mk{Fz7PyBC=AhHtk) z3*5}B-c@S!_tnj-8oHY)?95LSz4<;GpKml^UjR=UzZK=9-xo>4$3HE!(>PP*GwOe6 zln!vwEuNMB*FkCgXwW|C-~@#%YJ`_$=xlTNL|S}S@LxKZLkrIykGU$2p0t#V#jlS5 zTN=r>dUg_WzUxLoBG5Gas?Qrs2 zTh0fs7sK&cgD(bt%;2@aPZ`Yd<7)=D+Gk%MQfuA;XVy#d9_IE3=J}Mvgq?}(v zFb2nufZ2Z=@K=F-3M%Ty9y7i&_l@Lf2(E+8_OS1sqXv^+GnjPlJ;B1$5PSvy-wDi?sp;)C^q(f^uO{#U zbWt0g^R>m7Cot3BZ{nwX_Zv+5g9ej+2zxfL@VNTm9+6_w4;w7u4JQ2wgGuKek`4c~ z!K8B!$D&`iEb<#nI`@Js zy>V`YN#`DrrMDSOI`@Pu{cQ%5&Lc=mFB?oc_mC`ozrmz)Ps!51WH9MR3}*g#c&GfN zb00}D=~o&|I`@w(eFgS$6qC;VBgG7Vz+lq3FJ$Q%?AItJoqIou8Gh1W(zzF8>3a+& zoqI!;{;0vEbAQOv7h``$G3iSTX8OYhlU_EM^!)~te!yVTA2gWshYcqE81{c);c@+o z`$39HUx@u6#SG8=A;qNc!G4Eg(z#!xnDhe%lg|AkOMedQFvX;E?WLIE7Z^-B*J75w z!eG+5CbRVQ29wS;nx*eIm~^hwEd2$8N#{Du(z~(FQcOD6VTvjLT&$ZElg{;*V$wGo zEa43%{eFW<=eo>>f7D>oxlXh6Bd0}}bgt7ZeX+r$bKPd?D-9-{>o`lFG?;X*>nwf0 z!K8DYXX%FxCY|d(OMe3EJ;kJR{im4aGY@Mw#iVo1r1BgS=Niz`pEa0tuKg_i75t`8vBZxxoMOs9Y%u9u16ukKgGuKa(9#>RmQzf6 zlfjJtT?UhWi@~IS%V5&EHiU)8&0h<#mQzeR*M^E2e$rslxi+-)XACBtYeh?+hqawz z(z#Yt%=r5ZCY@_WOW$oU>0Cov`n?8|&b6ea&&T>uG3i`SDrWjT5mrn(*OQ7#f7)Qu zUon{ULs(xbKj~a=DklA?!K6QLFzJt;9p|5aPK3$7z+m#XEsoQ+z6A-b*H6w8B99Yp_cx_pGTN;36Zo>K)-dJ7E3ce19jqe%YU53tY)_n}?A1-(q|BDHJ3NnWF%>5J6PY14>^!bI# zhaf-YC7mC;LOYZ4nTPf0+ksghEZ~z+KK$I(0i@q3cvjF4{08U;D*v^>yG(q$fZt@& zI{^Fx=G9?;C!sMPu|I0(U?^PPUSp07Q*70B-h<^r*&nLBTCy4dg3j9ga z9+v~Z)1C?Xqcm(BxVwLjA1OLvSFSCAj0$*tQ z^Lv438GJUjyw*W~S>)rxx6y-#pbv6if#DwjzfJ#f;6DR>p5{M6|32_nO@4k0Z0j@S zwg6iY9R0B|mOb=rvFLc|D5CAFCqQ{@YuX)zW6pU z&eK}JF7^LYkI~WvzYP3-^fzLLU(kg0zmex`;N=MK-0Q@BCyUX);0>2shTA#<8 zfTQuKF(?B6Pm|y4fgeJ7XnVaAcnbBe^@q74R^FcjzQ&XXzN;VnD=^1rmhbbxcboS9 zY9jn8Xg?n``MU_%wx4d`Ul{qeu)Sga(DE(;pMdf_+vl$hTdsRedOJYBAM$YDl=2(| zw(<(5Ww+3Vh+*sQ#7%vp=~0IxAQOY~{yqyn;!Sei3*-@`Gd~zc&JZ)a2(N z@I5BJ4+7&emMwG9fLJ~c0o(XL1N=D?o}VMV6z!qa=jVTcz76_^jxXN>UV-+g@;(Rr zaWnq@68IMwKX&`@3&41uiT_2=Z$kT9?CEo`g>U6K3;6TsZ#?H@`F$K4qx_Y)w*OYp ze~0#Qz=wY;@W)L0e97kmlU^V2i)Q>R1D8$tYzMvx{g>s*^sx6Alcx^63;nC?=??+l zZ^C~8_+M@O$RDQImNnpKd4Dev-_L>n&dB>a(hAlV<>OF|HtzFH{hQd`iqJ9nxI#H zz?9FMfPZWFTY+n)JUW2WhTaFUkGr&{W z|Jm#Fa|HMq)W;c8o@tIHLDtCgebBK#Bg2R^YS-`(T ze|X%dw-`8u@?PZe3gGvneJ}BN9q{K+Uzd5j1^C-0{}teMSkLoZne}lKuubm}@EFG5 zJwE&cz_((2L$-wfGr+q|d3-Su{%8Up1HK*lozBOYn7MheBcil{a^#|H%)%?z&lNRW#EgEe~2OR?E^j!@~J*{C-A3H9~gs$ zeh=^=gxB`;QQ$HEo(|>tH1J8LKEDavkNVK|{v+T5=BpMTA9U&9J*l9D<2A?klQ0mi zLwnNtI}>=1k@q5CTR&?P_zK{^LjJGv@ed~G?*x8}k?;M$Poh8SeDw%$3Hpb&m&btL zXUgx(z&8Ew0)G(Uwfz1gLH|PnzaIM5>rDD*0e>FlbJ)w<1#IKr1iTdG&kn=#%|TII zhW^a<$MI?c^aoKsPzwa#0Q{QiZ|?=+41*7W{v*gseUtIs2fPdVlj6?+ztzb1b>N## z`D5)AlMnBX1}B>M8yQj1(8B-h-*bUqOuOGuFugYb|AQ&d#lR)g-&X?L{BHpMJoX3q zz6!%{2EHBg)et}N81TPCpL)sT9l+6gs4=(!*v9t(;Fr<9_^uMee>g$MS|;c)`NzAC z!O}KYzg*9J6PO=<)%O1{iSWMw{!3FHsEgolkw5Y?y=LrJS$W=^z*tN2VfCUu@uN!M zH6}iM(>Z!&bujlmD_enG2G|1#hk z7f0n&0{)=k-vw;rI|uVWzX62mllJ~z(0^{^{~y2~HRb&<@QG;OI-YzE7<-B>cS!ig z;5)zs(L2AwfTwzz+bg!Fb)~`5#ZvzYdHCHOlfjG59Xw#@~-46WkrhXp)zSYz}K4ui$Y4DT4Z-hL3KE7kX|K#5rW&a4|^3L<+636qe znHt|(E}lOzc+L585$V)q-reo#2>H>AS|@@75yJ>4ZQ{|vKT0rc>!=>@9`K4{`%jf}~ z-tG=u7RkFTMUP!ple1GuPLd?$5zc-Olp*KF>KUs^BMc*ZX`clUYog3}({{Hr0OyCmJMsw~T?N!)K= zlv_#V7I@{I`Nq)X)xz%b)qG)cGVgAxot)T#ODZQz-cE zo7?jn2jr4tjS1JmV;CDQZZ8cL^V_CyPxeTuSP5|H`!KGLb-hb&Tb}W*UcJ|PvRElijutD0a@Ae+yRq-m ze0Oht)8+0iS}nhsFNE)};_}~k&vy59m#X>l#L%_4TX%h5SEs+ITH`XqL@XEiJnGhs zU-^8HuSz*>?Cjf!(Joe0Lls_dUL3l%IyJ_2y%eZ0K3*(`EkmvrmdlrS=3RHd5HmGa z%%k|(NS5cjI$|S1{7PqEWZ|{j!)TMZu-M(a&bIAhHKOHCVSRlX?(b}y)=wgo-b|XweS<;{_g3{jtz46 zbnIg1u-e_!h-uN7)niCOYMLDsZ(APf zDn+PC8;b#&s1_t(vTa$(=ITo2^*V{HlT1*fEuh#y&M3_9o0+qS_XzB0!m#sFf!Q!*Gh4<~!$_49a_hjTa@x zai*s@)>9aVz7_V}u|jD)kj@?r?QS|_7RZD-0@1l{^TX~C<^*Jk17^8U#j2cB5bCl$Hi-2ObcVu@l&7kr zZfTMqoEjN{65pBYwfUYJ_bMc-4f+b#FmP3%OjD5-G3en&PNx~T=IJNLQS?=mE*8N7 z8$(PKH&!s#DH-Ec4U+sCAqx=F34($|m!$rlmn@D{3eYFVp@9~Lhw>aYD}^0yg^ft! z>mMg8SPJEG-fL%!NR~gtr79-iap)U?*D0|6q1HNy`FJ=#QFX(fF8M>@q!%OiUf)FW zC?Pgh({^Qij%)IiR*KAyTCuV*EpcOipN-DzUs0OcSU7*Pk#5@5b*T*ip<`1W@=DDk zlDZo7bgkPMt9NwoWr^-BV-ZU=)?i~}(P*JE4&^aFQ7NH+i6#Rbh|^na{E9@E1#4b2 z5c#uDx@ttgc!HyB4cHm0VAIRbLDQRBG|B30=Tud zq2azltOfLT#C!GD{N%{^0D96y1$&+Co$be8dBzGcu~G^b?Hp&q0_LDfv1GKKX>EzY zC|J9mJB6kWx0Y&thsO=oI%HPk5g%tcW2VVi7hOG7T`S8NZDVeW!Y0f*WrM{aJpxU2 zRAk3aM750l+I(Sqp;Ts94A4bmHN2_g@--MSGVw_Wf~a~C6NKt}JSSXNm~jlV-()z3 zp-P~W=d74(dtxe*?@wVXttNMfUDwQacC5FtggS<6&YLnl@f`!s6S~nh=@wSl=;BnH z%W4s#tzsLATL~4ndWuoIIFr%-gSgEO?4DpfgpEC}W!>l%%OVrU6o;8BaAS7V z1|r4MZPJT^-ExnC@lw8v|x*sWvo*`8jAU>_iN7@c+_w zXDv{KR0>>Wa8rLhtwP()h@Px(kDYmE!gg;Y4I>6bN!F zB~E}Y6ST6rF*_-Ts1P{+`G0-h1V)}G@{AS_PRj6UXbUgT_trUxDxaJb?6%^4$xpZj#zM8iJ>8 z^-PBLpmCG>>XC+k?-lB!aXbbOb(1#lI}LJGw(_ye_&fEJbGf~|c2 E2PM#Ys{jB1 diff --git a/panda/board/jungle/obj/panda_jungle.bin b/panda/board/jungle/obj/panda_jungle.bin deleted file mode 100755 index e10d78057348a8f0e643178a8df4ca043e541e6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55096 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 Ai~s-t diff --git a/panda/board/jungle/obj/panda_jungle.bin.signed b/panda/board/jungle/obj/panda_jungle.bin.signed deleted file mode 100644 index d1d77228d03579a01cc0c0bcb36f7e34fe2f42a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55232 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ⓈnC2!T{}WB7|AJ?Qsf)RpOs5S=>8B9kOn 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