diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index df7ca75195..c1be2c832c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -48,6 +48,8 @@ jobs: 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 @@ -76,7 +78,7 @@ jobs: - name: Build Docker image run: eval "$BUILD" - name: Run safety tests - timeout-minutes: 4 + timeout-minutes: 5 run: | ${{ env.RUN }} "cd .. && \ scons -c && \ @@ -85,23 +87,6 @@ jobs: scons -j$(nproc) ${{ matrix.flags }} && \ tests/safety/test.sh" - safety_replay: - name: safety replay - 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 replay - run: | - ${{ env.RUN }} "scons -j$(nproc) ${{ matrix.flags }} && \ - cd tests/safety_replay && \ - ./test_safety_replay.py" - misra: name: misra c2012 runs-on: ubuntu-20.04 diff --git a/.gitignore b/.gitignore index a3f6520bfd..1f96749b0c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ a.out .#* dist/ pandacan.egg-info/ -board/obj/ +obj/ examples/output.csv .DS_Store .vscode* diff --git a/Dockerfile b/Dockerfile index 7dc77ec72d..3da867ecb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,8 +51,8 @@ RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-instal ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}" ENV PANDA_PATH=/tmp/openpilot/panda -ENV OPENPILOT_REF="80bbba14f74e57bbe90216dfd0a99f6f68d77ca2" -ENV OPENDBC_REF="5880fbbccf5a670631b51836f20e446de643795a" +ENV OPENPILOT_REF="5690386d8d731c9bebda536a5c71c890f6dfe98c" +ENV OPENDBC_REF="12dd7675c5ab2f49aedb813a79e6131b370b379f" COPY requirements.txt /tmp/ RUN pyenv install 3.11.4 && \ @@ -80,13 +80,6 @@ RUN cd /tmp && \ rm -rf /tmp/openpilot/panda && \ rm -rf /tmp/tmppilot -RUN cd /tmp/openpilot && \ - git clone https://github.com/commaai/panda_jungle.git && \ - cd panda_jungle && \ - git fetch && \ - git checkout 3a791be1f1877a69cf45de16a670992380622297 && \ - rm -rf .git/ - 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 diff --git a/Jenkinsfile b/Jenkinsfile index 5d74e6e451..87f588a56f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,6 @@ def docker_run(String step_label, int timeout_mins, String cmd) { timeout(time: timeout_mins, unit: 'MINUTES') { sh script: "docker run --rm --privileged \ - --env PARTIAL_TESTS=${env.PARTIAL_TESTS} \ --env PYTHONWARNINGS=error \ --volume /dev/bus/usb:/dev/bus/usb \ --volume /var/run/dbus:/var/run/dbus \ @@ -62,7 +61,6 @@ pipeline { agent any environment { CI = "1" - //PARTIAL_TESTS = "${env.BRANCH_NAME == 'master' ? ' ' : '1'}" PYTHONWARNINGS= "error" DOCKER_IMAGE_TAG = "panda:build-${env.GIT_COMMIT}" @@ -83,6 +81,7 @@ pipeline { 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 --durations=0 [2-7]*.py -k 'not test_send_recv'"], ]) } @@ -94,11 +93,13 @@ pipeline { 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 --durations=0 2*.py [5-9]*.py"], ]) } } + /* stage ('Acquire resource locks') { options { lock(resource: "pandas") @@ -133,7 +134,8 @@ pipeline { stage('HITL tests') { steps { script { - docker_run("HITL tests", 35, 'PANDAS_JUNGLE=23002d000851393038373731 PANDAS_EXCLUDE="1d0002000c51303136383232 2f002e000c51303136383232" ./tests/hitl/test.sh') + 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') } } } @@ -146,6 +148,7 @@ pipeline { } } } + */ } } } diff --git a/README.md b/README.md index 1b325d801b..bb6f4fc206 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ sudo apt-get install dfu-util gcc-arm-none-eabi python3-pip libffi-dev git ```bash # macOS brew install --cask gcc-arm-embedded -brew install python3 dfu-util gcc@12 +brew install python3 dfu-util gcc@13 ``` Clone panda repository: @@ -112,7 +112,7 @@ to ensure that the behavior remains unchanged. * compiling the code and flashing it through USB. * receiving, sending, and forwarding CAN messages on all buses, over USB. -In addition, we run the [pylint](https://www.pylint.org/) and [flake8](https://github.com/PyCQA/flake8) linters on all python files within the panda repo. +In addition, we run the [ruff linter](https://github.com/astral-sh/ruff) on all python files within the panda repo. ## Hardware diff --git a/SConscript b/SConscript index ed3aff754d..34fddc49e5 100644 --- a/SConscript +++ b/SConscript @@ -1,6 +1,190 @@ +import os +import subprocess + + +PREFIX = "arm-none-eabi-" +BUILDER = "DEV" + +common_flags = [] + +panda_root = Dir('.').abspath + +if os.getenv("RELEASE"): + BUILD_TYPE = "RELEASE" + cert_fn = os.getenv("CERT") + assert cert_fn is not None, 'No certificate file specified. Please set CERT env variable' + assert os.path.exists(cert_fn), 'Certificate file not found. Please specify absolute path' +else: + BUILD_TYPE = "DEBUG" + cert_fn = File("./certs/debug").srcnode().abspath + common_flags += ["-DALLOW_DEBUG"] + + if os.getenv("DEBUG"): + common_flags += ["-DDEBUG"] + +def objcopy(source, target, env, for_signature): + return '$OBJCOPY -O binary %s %s' % (source[0], target[0]) + +def get_version(builder, build_type): + try: + git = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"], encoding='utf8').strip() + except subprocess.CalledProcessError: + git = "unknown" + return f"{builder}-{git}-{build_type}" + +def get_key_header(name): + from Crypto.PublicKey import RSA + + public_fn = File(f'./certs/{name}.pub').srcnode().abspath + with open(public_fn) as f: + rsa = RSA.importKey(f.read()) + assert(rsa.size_in_bits() == 1024) + + rr = pow(2**1024, 2, rsa.n) + n0inv = 2**32 - pow(rsa.n, -1, 2**32) + + r = [ + f"RSAPublicKey {name}_rsa_key = {{", + f" .len = 0x20,", + f" .n0inv = {n0inv}U,", + f" .n = {to_c_uint32(rsa.n)},", + f" .rr = {to_c_uint32(rr)},", + f" .exponent = {rsa.e},", + f"}};", + ] + return r + +def to_c_uint32(x): + nums = [] + for _ in range(0x20): + nums.append(x % (2**32)) + x //= (2**32) + return "{" + 'U,'.join(map(str, nums)) + "U}" + + +def build_project(project_name, project, extra_flags): + linkerscript_fn = File(project["LINKER_SCRIPT"]).srcnode().abspath + + flags = project["PROJECT_FLAGS"] + extra_flags + common_flags + [ + "-Wall", + "-Wextra", + "-Wstrict-prototypes", + "-Werror", + "-mlittle-endian", + "-mthumb", + "-nostdlib", + "-fno-builtin", + "-std=gnu11", + "-fmax-errors=3", + f"-T{linkerscript_fn}", + ] + + includes = [ + '.', + '..', + panda_root, + f"{panda_root}/board/", + f"{panda_root}/board/stm32fx/inc", + f"{panda_root}/board/stm32h7/inc", + ] + + env = Environment( + ENV=os.environ, + CC=PREFIX + 'gcc', + AS=PREFIX + 'gcc', + OBJCOPY=PREFIX + 'objcopy', + OBJDUMP=PREFIX + 'objdump', + CFLAGS=flags, + ASFLAGS=flags, + LINKFLAGS=flags, + CPPPATH=includes, + ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES", + BUILDERS={ + 'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf') + } + ) + + startup = env.Object(f"obj/startup_{project_name}", project["STARTUP_FILE"]) + + # Bootstub + crypto_obj = [ + env.Object(f"rsa-{project_name}", f"{panda_root}/crypto/rsa.c"), + env.Object(f"sha-{project_name}", f"{panda_root}/crypto/sha.c") + ] + bootstub_obj = env.Object(f"bootstub-{project_name}", File(project.get("BOOTSTUB", f"{panda_root}/board/bootstub.c"))) + bootstub_elf = env.Program(f"obj/bootstub.{project_name}.elf", + [startup] + crypto_obj + [bootstub_obj]) + env.Objcopy(f"obj/bootstub.{project_name}.bin", bootstub_elf) + + # Build main + main_obj = env.Object(f"main-{project_name}", project["MAIN"]) + main_elf = env.Program(f"obj/{project_name}.elf", [startup, main_obj], + LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) + main_bin = env.Objcopy(f"obj/{project_name}.bin", main_elf) + + # Sign main + sign_py = File(f"{panda_root}/crypto/sign.py").srcnode().abspath + env.Command(f"obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}") + + +base_project_f4 = { + "MAIN": "main.c", + "STARTUP_FILE": File("./board/stm32fx/startup_stm32f413xx.s"), + "LINKER_SCRIPT": File("./board/stm32fx/stm32f4_flash.ld"), + "APP_START_ADDRESS": "0x8004000", + "PROJECT_FLAGS": [ + "-mcpu=cortex-m4", + "-mhard-float", + "-DSTM32F4", + "-DSTM32F413xx", + "-mfpu=fpv4-sp-d16", + "-fsingle-precision-constant", + "-Os", + "-g", + ], +} + +base_project_h7 = { + "MAIN": "main.c", + "STARTUP_FILE": File("./board/stm32h7/startup_stm32h7x5xx.s"), + "LINKER_SCRIPT": File("./board/stm32h7/stm32h7x5_flash.ld"), + "APP_START_ADDRESS": "0x8020000", + "PROJECT_FLAGS": [ + "-mcpu=cortex-m7", + "-mhard-float", + "-DSTM32H7", + "-DSTM32H725xx", + "-mfpu=fpv5-d16", + "-fsingle-precision-constant", + "-Os", + "-g", + ], +} + +Export('base_project_f4', 'base_project_h7', 'build_project') + + +# Common autogenerated includes +with open("board/obj/gitversion.h", "w") as f: + f.write(f'const uint8_t gitversion[] = "{get_version(BUILDER, BUILD_TYPE)}";\n') + +with open("board/obj/version", "w") as f: + f.write(f'{get_version(BUILDER, BUILD_TYPE)}') + +certs = [get_key_header(n) for n in ["debug", "release"]] +with open("board/obj/cert.h", "w") as f: + for cert in certs: + f.write("\n".join(cert) + "\n") + # panda fw SConscript('board/SConscript') +# pedal fw +SConscript('board/pedal/SConscript') + +# panda jungle fw +SConscript('board/jungle/SConscript') + # test files -if GetOption('test'): +if GetOption('extras'): SConscript('tests/libpanda/SConscript') diff --git a/SConstruct b/SConstruct index 62f55787ba..2f25b44e59 100644 --- a/SConstruct +++ b/SConstruct @@ -1,7 +1,8 @@ -AddOption('--test', - action='store_true', +AddOption('--minimal', + action='store_false', + dest='extras', default=True, - help='build test files') + help='the minimum build. no tests, tools, etc.') AddOption('--ubsan', action='store_true', diff --git a/__init__.py b/__init__.py index dfb2bbf1c9..e8e05c52d5 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,11 @@ from .python.constants import McuType, BASEDIR, FW_PATH, USBPACKET_MAX_SIZE # noqa: F401 from .python.spi import PandaSpiException, PandaProtocolMismatch # noqa: F401 from .python.serial import PandaSerial # noqa: F401 +from .python.canhandle import CanHandle # noqa: F401 from .python import (Panda, PandaDFU, # noqa: F401 pack_can_buffer, unpack_can_buffer, calculate_checksum, unpack_log, DLC_TO_LEN, LEN_TO_DLC, ALTERNATIVE_EXPERIENCE, CANPACKET_HEAD_SIZE) + + +# panda jungle +from .board.jungle import PandaJungle, PandaJungleDFU # noqa: F401 diff --git a/board/SConscript b/board/SConscript index dc095e712a..45fa0f416e 100644 --- a/board/SConscript +++ b/board/SConscript @@ -1,194 +1,18 @@ import os import copy -import subprocess -PREFIX = "arm-none-eabi-" -BUILDER = "DEV" +Import('build_project', 'base_project_f4', 'base_project_h7') -common_flags = [] -build_projects = {} - -build_projects["pedal"] = { - "MAIN": "pedal/main.c", - "STARTUP_FILE": "stm32fx/startup_stm32f205xx.s", - "LINKER_SCRIPT": "stm32fx/stm32f2_flash.ld", - "APP_START_ADDRESS": "0x8004000", - "PROJECT_FLAGS": [ - "-mcpu=cortex-m3", - "-msoft-float", - "-DSTM32F2", - "-DSTM32F205xx", - "-O2", - "-DPEDAL", - ], -} - -build_projects["pedal_usb"] = copy.deepcopy(build_projects["pedal"]) -build_projects["pedal_usb"]["PROJECT_FLAGS"].append("-DPEDAL_USB") - -build_projects["panda"] = { - "MAIN": "main.c", - "STARTUP_FILE": "stm32fx/startup_stm32f413xx.s", - "LINKER_SCRIPT": "stm32fx/stm32f4_flash.ld", - "APP_START_ADDRESS": "0x8004000", - "PROJECT_FLAGS": [ - "-mcpu=cortex-m4", - "-mhard-float", - "-DSTM32F4", - "-DSTM32F413xx", - "-mfpu=fpv4-sp-d16", - "-fsingle-precision-constant", - "-Os", - "-g", - "-DPANDA", - ], +build_projects = { + "panda": base_project_f4, + "panda_h7": base_project_h7, } -build_projects["panda_h7"] = { - "MAIN": "main.c", - "STARTUP_FILE": "stm32h7/startup_stm32h7x5xx.s", - "LINKER_SCRIPT": "stm32h7/stm32h7x5_flash.ld", - "APP_START_ADDRESS": "0x8020000", - "PROJECT_FLAGS": [ - "-mcpu=cortex-m7", - "-mhard-float", - "-DSTM32H7", - "-DSTM32H725xx", - "-mfpu=fpv5-d16", - "-fsingle-precision-constant", - "-Os", - "-g", +for project_name, project in build_projects.items(): + flags = [ "-DPANDA", - ], -} - -if os.getenv("RELEASE"): - BUILD_TYPE = "RELEASE" - cert_fn = os.getenv("CERT") - assert cert_fn is not None, 'No certificate file specified. Please set CERT env variable' - assert os.path.exists(cert_fn), 'Certificate file not found. Please specify absolute path' -else: - BUILD_TYPE = "DEBUG" - cert_fn = File("../certs/debug").srcnode().abspath - common_flags += ["-DALLOW_DEBUG"] - -if os.getenv("DEBUG"): - common_flags += ["-DDEBUG"] - -includes = [ - "stm32fx/inc", - "stm32h7/inc", - "..", - ".", -] - -def get_version(builder, build_type): - try: - git = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"], encoding='utf8').strip() - except subprocess.CalledProcessError: - git = "unknown" - return f"{builder}-{git}-{build_type}" - - -def to_c_uint32(x): - nums = [] - for _ in range(0x20): - nums.append(x % (2**32)) - x //= (2**32) - return "{" + 'U,'.join(map(str, nums)) + "U}" - - -def get_key_header(name): - from Crypto.PublicKey import RSA - - public_fn = File(f'../certs/{name}.pub').srcnode().abspath - with open(public_fn) as f: - rsa = RSA.importKey(f.read()) - assert(rsa.size_in_bits() == 1024) - - rr = pow(2**1024, 2, rsa.n) - n0inv = 2**32 - pow(rsa.n, -1, 2**32) - - r = [ - f"RSAPublicKey {name}_rsa_key = {{", - f" .len = 0x20,", - f" .n0inv = {n0inv}U,", - f" .n = {to_c_uint32(rsa.n)},", - f" .rr = {to_c_uint32(rr)},", - f" .exponent = {rsa.e},", - f"}};", ] - return r - -def objcopy(source, target, env, for_signature): - return '$OBJCOPY -O binary %s %s' % (source[0], target[0]) - -# Common autogenerated includes -with open("obj/gitversion.h", "w") as f: - f.write(f'const uint8_t gitversion[] = "{get_version(BUILDER, BUILD_TYPE)}";\n') - -with open("obj/version", "w") as f: - f.write(f'{get_version(BUILDER, BUILD_TYPE)}') - -certs = [get_key_header(n) for n in ["debug", "release"]] -with open("obj/cert.h", "w") as f: - for cert in certs: - f.write("\n".join(cert) + "\n") - - -for project_name in build_projects: - project = build_projects[project_name] - linkerscript_fn = File(project["LINKER_SCRIPT"]).srcnode().abspath - - flags = [ - "-Wall", - "-Wextra", - "-Wstrict-prototypes", - "-Werror", - "-mlittle-endian", - "-mthumb", - "-nostdlib", - "-fno-builtin", - f"-T{linkerscript_fn}", - "-std=gnu11", - ] + project["PROJECT_FLAGS"] + common_flags - if ("ENABLE_SPI" in os.environ or "h7" in project_name) and not project_name.startswith('pedal'): flags.append('-DENABLE_SPI') - project_env = Environment( - ENV=os.environ, - CC=PREFIX + 'gcc', - AS=PREFIX + 'gcc', - OBJCOPY=PREFIX + 'objcopy', - OBJDUMP=PREFIX + 'objdump', - ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES", - CFLAGS=flags, - ASFLAGS=flags, - LINKFLAGS=flags, - CPPPATH=includes, - BUILDERS={ - 'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf') - } - ) - - startup = project_env.Object(f"obj/startup_{project_name}", project["STARTUP_FILE"]) - - # Bootstub - crypto_obj = [ - project_env.Object(f"rsa-{project_name}", "../crypto/rsa.c"), - project_env.Object(f"sha-{project_name}", "../crypto/sha.c") - ] - bootstub_obj = project_env.Object(f"bootstub-{project_name}", "bootstub.c") - bootstub_elf = project_env.Program(f"obj/bootstub.{project_name}.elf", [startup] + crypto_obj + [bootstub_obj]) - bootstub_bin = project_env.Objcopy(f"obj/bootstub.{project_name}.bin", bootstub_elf) - - # Build main - main_obj = project_env.Object(f"main-{project_name}", project["MAIN"]) - main_elf = project_env.Program(f"obj/{project_name}.elf", [startup, main_obj], - LINKFLAGS=[f"-Wl,--section-start,.isr_vector={project['APP_START_ADDRESS']}"] + flags) - main_bin = project_env.Objcopy(f"obj/{project_name}.bin", main_elf) - - # Sign main - sign_py = File("../crypto/sign.py").srcnode().abspath - panda_bin_signed = project_env.Command(f"obj/{project_name}.bin.signed", main_bin, f"SETLEN=1 {sign_py} $SOURCE $TARGET {cert_fn}") + build_project(project_name, project, flags) diff --git a/board/boards/black.h b/board/boards/black.h index 216def774f..80b265394e 100644 --- a/board/boards/black.h +++ b/board/boards/black.h @@ -49,37 +49,13 @@ void black_set_led(uint8_t color, bool enabled) { } } -void black_set_gps_load_switch(bool enabled) { - set_gpio_output(GPIOC, 12, enabled); -} - void black_set_usb_load_switch(bool enabled) { set_gpio_output(GPIOB, 1, !enabled); } -void black_set_gps_mode(uint8_t mode) { - switch (mode) { - case GPS_DISABLED: - // GPS OFF - set_gpio_output(GPIOC, 12, 0); - set_gpio_output(GPIOC, 5, 0); - break; - case GPS_ENABLED: - // GPS ON - set_gpio_output(GPIOC, 12, 1); - set_gpio_output(GPIOC, 5, 1); - break; - case GPS_BOOTMODE: - set_gpio_output(GPIOC, 12, 1); - set_gpio_output(GPIOC, 5, 0); - break; - default: - print("Invalid GPS mode\n"); - break; - } -} - -void black_set_can_mode(uint8_t mode){ +void black_set_can_mode(uint8_t mode) { + black_enable_can_transceiver(2U, false); + black_enable_can_transceiver(4U, false); switch (mode) { case CAN_MODE_NORMAL: case CAN_MODE_OBD_CAN2: @@ -91,6 +67,8 @@ void black_set_can_mode(uint8_t mode){ // B5,B6: normal CAN2 mode set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + black_enable_can_transceiver(2U, true); + } else { // B5,B6: disable normal CAN2 mode set_gpio_mode(GPIOB, 5, MODE_INPUT); @@ -99,6 +77,7 @@ void black_set_can_mode(uint8_t mode){ // B12,B13: OBD mode set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + black_enable_can_transceiver(4U, true); } break; default: @@ -124,8 +103,9 @@ void black_init(void) { set_gpio_mode(GPIOC, 0, MODE_ANALOG); set_gpio_mode(GPIOC, 3, MODE_ANALOG); - // Set default state of GPS - current_board->set_gps_mode(GPS_ENABLED); + // GPS OFF + 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) @@ -136,9 +116,6 @@ void black_init(void) { set_gpio_output(GPIOC, 10, 1); set_gpio_output(GPIOC, 11, 1); - // Turn on GPS load switch. - black_set_gps_load_switch(true); - // Turn on USB load switch. black_set_usb_load_switch(true); @@ -165,6 +142,12 @@ void black_init(void) { } } +void black_init_bootloader(void) { + // GPS OFF + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 12, 0); +} + const harness_configuration black_harness_config = { .has_harness = true, .GPIO_SBU1 = GPIOC, @@ -183,7 +166,6 @@ const board board_black = { .board_type = "Black", .board_tick = unused_board_tick, .harness_config = &black_harness_config, - .has_gps = true, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -195,10 +177,10 @@ const board board_black = { .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, .init = black_init, + .init_bootloader = black_init_bootloader, .enable_can_transceiver = black_enable_can_transceiver, .enable_can_transceivers = black_enable_can_transceivers, .set_led = black_set_led, - .set_gps_mode = black_set_gps_mode, .set_can_mode = black_set_can_mode, .check_ignition = black_check_ignition, .read_current = unused_read_current, diff --git a/board/boards/board_declarations.h b/board/boards/board_declarations.h index c0c6436906..e8b1cdcffa 100644 --- a/board/boards/board_declarations.h +++ b/board/boards/board_declarations.h @@ -1,9 +1,9 @@ // ******************** Prototypes ******************** typedef void (*board_init)(void); +typedef void (*board_init_bootloader)(void); typedef void (*board_enable_can_transceiver)(uint8_t transceiver, bool enabled); typedef void (*board_enable_can_transceivers)(bool enabled); typedef void (*board_set_led)(uint8_t color, bool enabled); -typedef void (*board_set_gps_mode)(uint8_t mode); typedef void (*board_set_can_mode)(uint8_t mode); typedef bool (*board_check_ignition)(void); typedef uint32_t (*board_read_current)(void); @@ -17,7 +17,6 @@ typedef bool (*board_read_som_gpio)(void); struct board { const char *board_type; const harness_configuration *harness_config; - const bool has_gps; const bool has_hw_gmlan; const bool has_obd; const bool has_lin; @@ -29,10 +28,10 @@ struct board { const bool fan_stall_recovery; const uint8_t fan_enable_cooldown_time; board_init init; + board_init_bootloader init_bootloader; board_enable_can_transceiver enable_can_transceiver; board_enable_can_transceivers enable_can_transceivers; board_set_led set_led; - board_set_gps_mode set_gps_mode; board_set_can_mode set_can_mode; board_check_ignition check_ignition; board_read_current read_current; @@ -68,11 +67,6 @@ struct board { #define USB_POWER_CDP 2U #define USB_POWER_DCP 3U -// GPS modes -#define GPS_DISABLED 0U -#define GPS_ENABLED 1U -#define GPS_BOOTMODE 2U - // CAN modes #define CAN_MODE_NORMAL 0U #define CAN_MODE_GMLAN_CAN2 1U diff --git a/board/boards/dos.h b/board/boards/dos.h index 0178ff016f..7e0a270036 100644 --- a/board/boards/dos.h +++ b/board/boards/dos.h @@ -68,7 +68,9 @@ bool dos_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen, bool harn return ret; } -void dos_set_can_mode(uint8_t mode){ +void dos_set_can_mode(uint8_t mode) { + dos_enable_can_transceiver(2U, false); + dos_enable_can_transceiver(4U, false); switch (mode) { case CAN_MODE_NORMAL: case CAN_MODE_OBD_CAN2: @@ -80,6 +82,7 @@ void dos_set_can_mode(uint8_t mode){ // B5,B6: normal CAN2 mode set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2); set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2); + dos_enable_can_transceiver(2U, true); } else { // B5,B6: disable normal CAN2 mode set_gpio_mode(GPIOB, 5, MODE_INPUT); @@ -88,6 +91,7 @@ void dos_set_can_mode(uint8_t mode){ // B12,B13: OBD mode set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2); set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2); + dos_enable_can_transceiver(4U, true); } break; default: @@ -206,7 +210,6 @@ const board board_dos = { .board_type = "Dos", .board_tick = dos_board_tick, .harness_config = &dos_harness_config, - .has_gps = false, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -222,10 +225,10 @@ const board board_dos = { .fan_stall_recovery = true, .fan_enable_cooldown_time = 3U, .init = dos_init, + .init_bootloader = unused_init_bootloader, .enable_can_transceiver = dos_enable_can_transceiver, .enable_can_transceivers = dos_enable_can_transceivers, .set_led = dos_set_led, - .set_gps_mode = unused_set_gps_mode, .set_can_mode = dos_set_can_mode, .check_ignition = dos_check_ignition, .read_current = unused_read_current, diff --git a/board/boards/grey.h b/board/boards/grey.h index 0adb5e2a1c..a74f894480 100644 --- a/board/boards/grey.h +++ b/board/boards/grey.h @@ -4,40 +4,10 @@ // Most hardware functionality is similar to white panda -void grey_init(void) { - white_grey_common_init(); - - // Set default state of GPS - current_board->set_gps_mode(GPS_ENABLED); -} - -void grey_set_gps_mode(uint8_t mode) { - switch (mode) { - case GPS_DISABLED: - // GPS OFF - set_gpio_output(GPIOC, 14, 0); - set_gpio_output(GPIOC, 5, 0); - break; - case GPS_ENABLED: - // GPS ON - set_gpio_output(GPIOC, 14, 1); - set_gpio_output(GPIOC, 5, 1); - break; - case GPS_BOOTMODE: - set_gpio_output(GPIOC, 14, 1); - set_gpio_output(GPIOC, 5, 0); - break; - default: - print("Invalid ESP/GPS mode\n"); - break; - } -} - const board board_grey = { .board_type = "Grey", .board_tick = unused_board_tick, .harness_config = &white_harness_config, - .has_gps = true, .has_hw_gmlan = true, .has_obd = false, .has_lin = true, @@ -48,11 +18,11 @@ const board board_grey = { .avdd_mV = 3300U, .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, - .init = grey_init, + .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_gps_mode = grey_set_gps_mode, .set_can_mode = white_set_can_mode, .check_ignition = white_check_ignition, .read_current = white_read_current, diff --git a/board/boards/pedal.h b/board/boards/pedal.h index d545a9faf1..f2ae367e5d 100644 --- a/board/boards/pedal.h +++ b/board/boards/pedal.h @@ -30,11 +30,6 @@ void pedal_set_led(uint8_t color, bool enabled) { } } -void pedal_set_gps_mode(uint8_t mode) { - UNUSED(mode); - print("Trying to set ESP/GPS mode on pedal. This is not supported.\n"); -} - void pedal_set_can_mode(uint8_t mode){ switch (mode) { case CAN_MODE_NORMAL: @@ -75,7 +70,6 @@ const board board_pedal = { .board_type = "Pedal", .board_tick = unused_board_tick, .harness_config = &pedal_harness_config, - .has_gps = false, .has_hw_gmlan = false, .has_obd = false, .has_lin = false, @@ -87,10 +81,10 @@ const board board_pedal = { .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, .init = pedal_init, + .init_bootloader = unused_init_bootloader, .enable_can_transceiver = pedal_enable_can_transceiver, .enable_can_transceivers = pedal_enable_can_transceivers, .set_led = pedal_set_led, - .set_gps_mode = pedal_set_gps_mode, .set_can_mode = pedal_set_can_mode, .check_ignition = pedal_check_ignition, .read_current = unused_read_current, diff --git a/board/boards/red.h b/board/boards/red.h index e7dccc00ed..987554698e 100644 --- a/board/boards/red.h +++ b/board/boards/red.h @@ -50,6 +50,8 @@ void red_set_led(uint8_t color, bool enabled) { } void red_set_can_mode(uint8_t mode) { + red_enable_can_transceiver(2U, false); + red_enable_can_transceiver(4U, false); switch (mode) { case CAN_MODE_NORMAL: case CAN_MODE_OBD_CAN2: @@ -67,6 +69,7 @@ void red_set_can_mode(uint8_t mode) { set_gpio_pullup(GPIOB, 6, PULL_NONE); set_gpio_alternate(GPIOB, 6, GPIO_AF9_FDCAN2); + red_enable_can_transceiver(2U, true); } else { // B5,B6: disable normal mode set_gpio_pullup(GPIOB, 5, PULL_NONE); @@ -80,6 +83,7 @@ void red_set_can_mode(uint8_t mode) { set_gpio_pullup(GPIOB, 13, PULL_NONE); set_gpio_alternate(GPIOB, 13, GPIO_AF9_FDCAN2); + red_enable_can_transceiver(4U, true); } break; default: @@ -170,7 +174,6 @@ const board board_red = { .board_type = "Red", .board_tick = unused_board_tick, .harness_config = &red_harness_config, - .has_gps = false, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -182,10 +185,10 @@ const board board_red = { .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, .init = red_init, + .init_bootloader = unused_init_bootloader, .enable_can_transceiver = red_enable_can_transceiver, .enable_can_transceivers = red_enable_can_transceivers, .set_led = red_set_led, - .set_gps_mode = unused_set_gps_mode, .set_can_mode = red_set_can_mode, .check_ignition = red_check_ignition, .read_current = unused_read_current, diff --git a/board/boards/red_chiplet.h b/board/boards/red_chiplet.h index 8a3f8dbd98..1301cd3e54 100644 --- a/board/boards/red_chiplet.h +++ b/board/boards/red_chiplet.h @@ -35,6 +35,48 @@ void red_chiplet_enable_can_transceivers(bool enabled) { } } +void red_chiplet_set_can_mode(uint8_t mode) { + red_chiplet_enable_can_transceiver(2U, false); + red_chiplet_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 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); + red_chiplet_enable_can_transceiver(2U, true); + } else { + // 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); + red_chiplet_enable_can_transceiver(4U, true); + } + break; + default: + break; + } +} + void red_chiplet_set_fan_or_usb_load_switch(bool enabled) { set_gpio_output(GPIOD, 3, enabled); } @@ -89,7 +131,7 @@ void red_chiplet_init(void) { red_set_led(LED_BLUE, false); // Set normal CAN mode - red_set_can_mode(CAN_MODE_NORMAL); + red_chiplet_set_can_mode(CAN_MODE_NORMAL); // flip CAN0 and CAN2 if we are flipped if (harness.status == HARNESS_STATUS_FLIPPED) { diff --git a/board/boards/red_v2.h b/board/boards/red_v2.h index d6724d8bb7..df663032b0 100644 --- a/board/boards/red_v2.h +++ b/board/boards/red_v2.h @@ -14,7 +14,6 @@ const board board_red_v2 = { .board_type = "Red_v2", .board_tick = unused_board_tick, .harness_config = &red_chiplet_harness_config, - .has_gps = false, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -26,11 +25,11 @@ const board board_red_v2 = { .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, .init = red_panda_v2_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_gps_mode = unused_set_gps_mode, - .set_can_mode = red_set_can_mode, + .set_can_mode = red_chiplet_set_can_mode, .check_ignition = red_check_ignition, .read_current = unused_read_current, .set_fan_enabled = unused_set_fan_enabled, diff --git a/board/boards/tres.h b/board/boards/tres.h index 97dac7aa0f..c1f1f9ec22 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -89,7 +89,6 @@ const board board_tres = { .board_type = "Tres", .board_tick = tres_board_tick, .harness_config = &red_chiplet_harness_config, - .has_gps = false, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -101,11 +100,11 @@ const board board_tres = { .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_gps_mode = unused_set_gps_mode, - .set_can_mode = red_set_can_mode, + .set_can_mode = red_chiplet_set_can_mode, .check_ignition = red_check_ignition, .read_current = unused_read_current, .set_fan_enabled = tres_set_fan_enabled, diff --git a/board/boards/uno.h b/board/boards/uno.h index ad773f6e49..27c2e40297 100644 --- a/board/boards/uno.h +++ b/board/boards/uno.h @@ -51,10 +51,6 @@ void uno_set_led(uint8_t color, bool enabled) { } } -void uno_set_gps_load_switch(bool enabled) { - set_gpio_output(GPIOC, 12, enabled); -} - void uno_set_bootkick(bool enabled){ if (enabled) { set_gpio_output(GPIOB, 14, false); @@ -73,32 +69,9 @@ void uno_set_phone_power(bool enabled){ set_gpio_output(GPIOB, 4, enabled); } -void uno_set_gps_mode(uint8_t mode) { - switch (mode) { - case GPS_DISABLED: - // GPS OFF - set_gpio_output(GPIOB, 1, 0); - set_gpio_output(GPIOC, 5, 0); - uno_set_gps_load_switch(false); - break; - case GPS_ENABLED: - // GPS ON - set_gpio_output(GPIOB, 1, 1); - set_gpio_output(GPIOC, 5, 1); - uno_set_gps_load_switch(true); - break; - case GPS_BOOTMODE: - set_gpio_output(GPIOB, 1, 1); - set_gpio_output(GPIOC, 5, 0); - uno_set_gps_load_switch(true); - break; - default: - print("Invalid ESP/GPS mode\n"); - break; - } -} - -void uno_set_can_mode(uint8_t mode){ +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: @@ -110,6 +83,7 @@ void uno_set_can_mode(uint8_t mode){ // 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); @@ -118,6 +92,7 @@ void uno_set_can_mode(uint8_t mode){ // 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: @@ -168,8 +143,10 @@ void uno_init(void) { set_gpio_mode(GPIOC, 0, MODE_ANALOG); set_gpio_mode(GPIOC, 3, MODE_ANALOG); - // Set default state of GPS - current_board->set_gps_mode(GPS_ENABLED); + // 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) @@ -183,9 +160,6 @@ void uno_init(void) { // C8: FAN PWM aka TIM3_CH3 set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3); - // Turn on GPS load switch. - uno_set_gps_load_switch(true); - // Turn on phone regulator uno_set_phone_power(true); @@ -227,6 +201,13 @@ void uno_init(void) { uno_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, @@ -245,7 +226,6 @@ const board board_uno = { .board_type = "Uno", .board_tick = uno_board_tick, .harness_config = &uno_harness_config, - .has_gps = true, .has_hw_gmlan = false, .has_obd = true, .has_lin = false, @@ -257,10 +237,10 @@ const board board_uno = { .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_gps_mode = uno_set_gps_mode, .set_can_mode = uno_set_can_mode, .check_ignition = uno_check_ignition, .read_current = unused_read_current, diff --git a/board/boards/unused_funcs.h b/board/boards/unused_funcs.h index 4e6214e6a7..24279d2bf9 100644 --- a/board/boards/unused_funcs.h +++ b/board/boards/unused_funcs.h @@ -1,5 +1,4 @@ -void unused_set_gps_mode(uint8_t mode) { - UNUSED(mode); +void unused_init_bootloader(void) { } void unused_set_ir_power(uint8_t percentage) { diff --git a/board/boards/white.h b/board/boards/white.h index 45d860181b..f2dedf0107 100644 --- a/board/boards/white.h +++ b/board/boards/white.h @@ -65,23 +65,6 @@ void white_set_usb_power_mode(uint8_t mode){ } } -void white_set_gps_mode(uint8_t mode) { - switch (mode) { - case GPS_DISABLED: - // ESP OFF - set_gpio_output(GPIOC, 14, 0); - set_gpio_output(GPIOC, 5, 0); - break; - case GPS_BOOTMODE: - set_gpio_output(GPIOC, 14, 1); - set_gpio_output(GPIOC, 5, 0); - break; - default: - print("Invalid ESP/GPS mode\n"); - break; - } -} - void white_set_can_mode(uint8_t mode){ switch (mode) { case CAN_MODE_NORMAL: @@ -150,7 +133,7 @@ bool white_check_ignition(void){ return !get_gpio_input(GPIOA, 1); } -void white_grey_common_init(void) { +void white_grey_init(void) { common_init_gpio(); // C3: current sense @@ -222,13 +205,16 @@ void white_grey_common_init(void) { } else { white_set_usb_power_mode(USB_POWER_CLIENT); } -} -void white_init(void) { - white_grey_common_init(); + // ESP/GPS off + set_gpio_output(GPIOC, 5, 0); + set_gpio_output(GPIOC, 14, 0); +} - // Set ESP off by default - current_board->set_gps_mode(GPS_DISABLED); +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 = { @@ -239,7 +225,6 @@ const board board_white = { .board_type = "White", .board_tick = unused_board_tick, .harness_config = &white_harness_config, - .has_gps = false, .has_hw_gmlan = true, .has_obd = false, .has_lin = true, @@ -250,11 +235,11 @@ const board board_white = { .avdd_mV = 3300U, .fan_stall_recovery = false, .fan_enable_cooldown_time = 0U, - .init = white_init, + .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_gps_mode = white_set_gps_mode, .set_can_mode = white_set_can_mode, .check_ignition = white_check_ignition, .read_current = white_read_current, diff --git a/board/bootstub.c b/board/bootstub.c index a2bf7dad7a..e2977e938c 100644 --- a/board/bootstub.c +++ b/board/bootstub.c @@ -41,6 +41,10 @@ int main(void) { clock_init(); detect_board_type(); +#ifdef PANDA_JUNGLE + current_board->set_panda_power(true); +#endif + if (enter_bootloader_mode == ENTER_SOFTLOADER_MAGIC) { enter_bootloader_mode = 0; soft_flasher_start(); diff --git a/board/config.h b/board/config.h index 903161876c..507d19e7d3 100644 --- a/board/config.h +++ b/board/config.h @@ -21,10 +21,18 @@ // USB definitions #define USB_VID 0xBBAAU -#ifdef BOOTSTUB - #define USB_PID 0xDDEEU +#ifdef PANDA_JUNGLE + #ifdef BOOTSTUB + #define USB_PID 0xDDEFU + #else + #define USB_PID 0xDDCFU + #endif #else - #define USB_PID 0xDDCCU + #ifdef BOOTSTUB + #define USB_PID 0xDDEEU + #else + #define USB_PID 0xDDCCU + #endif #endif // platform includes diff --git a/board/debug/README.md b/board/debug/README.md new file mode 100644 index 0000000000..d89eab818a --- /dev/null +++ b/board/debug/README.md @@ -0,0 +1,26 @@ +# In-circuit debugging using openocd and gdb + +## Hardware +Connect an ST-Link V2 programmer to the SWD pins on the board. The pins that need to be connected are: +- GND +- VTref +- SWDIO +- SWCLK +- NRST + +Make sure you're using a genuine one for boards that do not have a 3.3V panda power rail. For example, the tres runs at 1.8V, which is not supported by the clones. + +## Openocd +Install openocd. For Ubuntu 20.04, the one in the package manager works fine: `sudo apt install openocd`. + +To run, use `./debug_f4.sh (TODO)` or `./debug_h7.sh` depending on the panda. + +## GDB +You need `gdb-multiarch`. + +Once openocd is running, you can connect from gdb as follows: +``` +$ gdb-multiarch +(gdb) target ext :3333 +``` +To reset and break, use `monitor reset halt`. diff --git a/board/debug/debug_h7.sh b/board/debug/debug_h7.sh new file mode 100755 index 0000000000..a2bd88434e --- /dev/null +++ b/board/debug/debug_h7.sh @@ -0,0 +1,3 @@ +#!/bin/bash -e + +sudo openocd -f "interface/stlink.cfg" -c "transport select hla_swd" -f "target/stm32h7x.cfg" -c "init" diff --git a/board/drivers/bxcan.h b/board/drivers/bxcan.h index 478bd5858b..080c9f4a9d 100644 --- a/board/drivers/bxcan.h +++ b/board/drivers/bxcan.h @@ -11,11 +11,11 @@ uint8_t can_irq_number[3][3] = { bool can_set_speed(uint8_t can_number) { bool ret = true; - CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); ret &= llcan_set_speed( - CAN, + CANx, bus_config[bus_number].can_speed, can_loopback, (unsigned int)(can_silent) & (1U << can_number) @@ -74,8 +74,8 @@ void can_set_gmlan(uint8_t bus) { } void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { - CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); - uint32_t esr_reg = CAN->ESR; + 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; @@ -98,12 +98,12 @@ void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { can_health[can_number].total_error_cnt += 1U; // RX message lost due to FIFO overrun - if ((CAN->RF0R & (CAN_RF0R_FOVR0)) != 0) { + if ((CANx->RF0R & (CAN_RF0R_FOVR0)) != 0) { can_health[can_number].total_rx_lost_cnt += 1U; - CAN->RF0R &= ~(CAN_RF0R_FOVR0); + CANx->RF0R &= ~(CAN_RF0R_FOVR0); } can_health[can_number].can_core_reset_cnt += 1U; - llcan_clear_send(CAN); + llcan_clear_send(CANx); } } @@ -119,28 +119,28 @@ void process_can(uint8_t can_number) { ENTER_CRITICAL(); - CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); + 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 ((CAN->TSR & (CAN_TSR_TERR0 | CAN_TSR_ALST0)) != 0) { // last TX failed due to error arbitration lost + 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; - CAN->TSR |= (CAN_TSR_TERR0 | CAN_TSR_ALST0); + CANx->TSR |= (CAN_TSR_TERR0 | CAN_TSR_ALST0); } - if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { + if ((CANx->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { // add successfully transmitted message to my fifo - if ((CAN->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) { - if ((CAN->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) { + 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 = (CAN->sTxMailBox[0].TIR >> 2) & 0x1U; - to_push.addr = (to_push.extended != 0U) ? (CAN->sTxMailBox[0].TIR >> 3) : (CAN->sTxMailBox[0].TIR >> 21); - to_push.data_len_code = CAN->sTxMailBox[0].TDTR & 0xFU; + 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], CAN->sTxMailBox[0].TDLR); - WORD_TO_BYTE_ARRAY(&to_push.data[4], CAN->sTxMailBox[0].TDHR); + 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; @@ -148,19 +148,19 @@ void process_can(uint8_t can_number) { // clear interrupt // careful, this can also be cleared by requesting a transmission - CAN->TSR |= CAN_TSR_RQCP0; + 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 - CAN->sTxMailBox[0].TIR = ((to_send.extended != 0U) ? (to_send.addr << 3) : (to_send.addr << 21)) | (to_send.extended << 2); - CAN->sTxMailBox[0].TDTR = to_send.data_len_code; - BYTE_ARRAY_TO_WORD(CAN->sTxMailBox[0].TDLR, &to_send.data[0]); - BYTE_ARRAY_TO_WORD(CAN->sTxMailBox[0].TDHR, &to_send.data[4]); + 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 - CAN->sTxMailBox[0].TIR |= 0x1U; + CANx->sTxMailBox[0].TIR |= 0x1U; } else { can_health[can_number].total_tx_checksum_error_cnt += 1U; } @@ -176,10 +176,10 @@ void process_can(uint8_t can_number) { // CANx_RX0 IRQ Handler // blink blue when we are receiving CAN messages void can_rx(uint8_t can_number) { - CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - while ((CAN->RF0R & CAN_RF0R_FMP0) != 0) { + while ((CANx->RF0R & CAN_RF0R_FMP0) != 0) { can_health[can_number].total_rx_cnt += 1U; // can is live @@ -190,12 +190,12 @@ void can_rx(uint8_t can_number) { to_push.returned = 0U; to_push.rejected = 0U; - to_push.extended = (CAN->sFIFOMailBox[0].RIR >> 2) & 0x1U; - to_push.addr = (to_push.extended != 0U) ? (CAN->sFIFOMailBox[0].RIR >> 3) : (CAN->sFIFOMailBox[0].RIR >> 21); - to_push.data_len_code = CAN->sFIFOMailBox[0].RDTR & 0xFU; + 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], CAN->sFIFOMailBox[0].RDLR); - WORD_TO_BYTE_ARRAY(&to_push.data[4], CAN->sFIFOMailBox[0].RDHR); + 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) @@ -223,7 +223,7 @@ void can_rx(uint8_t can_number) { rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U; // next - CAN->RF0R |= CAN_RF0R_RFOM0; + CANx->RF0R |= CAN_RF0R_RFOM0; } } @@ -253,9 +253,9 @@ bool can_init(uint8_t can_number) { REGISTER_INTERRUPT(CAN3_SCE_IRQn, CAN3_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) if (can_number != 0xffU) { - CAN_TypeDef *CAN = CANIF_FROM_CAN_NUM(can_number); + CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); ret &= can_set_speed(can_number); - ret &= llcan_init(CAN); + ret &= llcan_init(CANx); // in case there are queued up messages process_can(can_number); } diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 8a2851691b..8a92d5282c 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -8,6 +8,7 @@ typedef struct { 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; @@ -51,17 +52,22 @@ void process_can(uint8_t can_number); 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 -__attribute__((section(".ram_d1"))) can_buffer(rx_q, 0x1000) -__attribute__((section(".ram_d1"))) can_buffer(tx2_q, 0x1A0) -__attribute__((section(".ram_d2"))) can_buffer(txgmlan_q, 0x1A0) +// 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, 0x1000) -can_buffer(tx2_q, 0x1A0) -can_buffer(txgmlan_q, 0x1A0) +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(tx1_q, 0x1A0) -can_buffer(tx3_q, 0x1A0) +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}; @@ -157,14 +163,15 @@ void can_clear(can_ring *q) { // 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, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, - { .bus_lookup = 1U, .can_num_lookup = 1U, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, - { .bus_lookup = 2U, .can_num_lookup = 2U, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, - { .bus_lookup = 0xFFU, .can_num_lookup = 0xFFU, .can_speed = 333U, .can_data_speed = 333U, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, + { .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]) @@ -190,6 +197,10 @@ void can_flip_buses(uint8_t bus1, uint8_t 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); diff --git a/board/drivers/clock_source.h b/board/drivers/clock_source.h index d1b5724f16..11b2fa3247 100644 --- a/board/drivers/clock_source.h +++ b/board/drivers/clock_source.h @@ -1,6 +1,10 @@ #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 diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index 7cab232d36..34367cb3a9 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -21,11 +21,11 @@ uint8_t can_irq_number[3][2] = { bool can_set_speed(uint8_t can_number) { bool ret = true; - FDCAN_GlobalTypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); ret &= llcan_set_speed( - CANx, + FDCANx, bus_config[bus_number].can_speed, bus_config[bus_number].can_data_speed, bus_config[bus_number].canfd_non_iso, @@ -41,9 +41,9 @@ void can_set_gmlan(uint8_t bus) { } void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { - FDCAN_GlobalTypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); - uint32_t psr_reg = CANx->PSR; - uint32_t ecr_reg = CANx->ECR; + 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; @@ -69,7 +69,7 @@ void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { if (ir_reg != 0U) { // Clear error interrupts - CANx->IR |= (FDCAN_IR_PED | FDCAN_IR_PEA | FDCAN_IR_EP | FDCAN_IR_BO | FDCAN_IR_RF0L); + 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) { @@ -79,24 +79,24 @@ void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) { // By resseting CAN core when no ACK is detected for a while(until TEC counter reaches 127) it can recover faster 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)) { can_health[can_number].can_core_reset_cnt += 1U; - can_health[can_number].total_tx_lost_cnt += (FDCAN_TX_FIFO_EL_CNT - (CANx->TXFQS & FDCAN_TXFQS_TFFL)); // TX FIFO msgs will be lost after reset - llcan_clear_send(CANx); + 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 ***************************** -// FDCANx_IT1 IRQ Handler (TX) +// FDFDCANx_IT1 IRQ Handler (TX) void process_can(uint8_t can_number) { if (can_number != 0xffU) { ENTER_CRITICAL(); - FDCAN_GlobalTypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); - CANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag + FDCANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag - if ((CANx->TXFQS & FDCAN_TXFQS_TFQF) == 0) { + 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)) { @@ -104,7 +104,7 @@ void process_can(uint8_t can_number) { 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) - uint8_t tx_index = (CANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1F; + uint8_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)); @@ -118,7 +118,7 @@ void process_can(uint8_t can_number) { BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4U]); } - CANx->TXBAR = (1UL << tx_index); + FDCANx->TXBAR = (1UL << tx_index); // Send back to USB CANPacket_t to_push; @@ -144,27 +144,27 @@ void process_can(uint8_t can_number) { } } -// FDCANx_IT0 IRQ Handler (RX and errors) +// FDFDCANx_IT0 IRQ Handler (RX and errors) // blink blue when we are receiving CAN messages void can_rx(uint8_t can_number) { - FDCAN_GlobalTypeDef *CANx = CANIF_FROM_CAN_NUM(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 = CANx->IR; + uint32_t ir_reg = FDCANx->IR; // Clear all new messages from Rx FIFO 0 - CANx->IR |= FDCAN_IR_RF0N; - while((CANx->RXF0S & FDCAN_RXF0S_F0FL) != 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) - uint8_t rx_fifo_idx = (uint8_t)((CANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3F); + uint8_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((CANx->RXF0S & FDCAN_RXF0S_F0F) == FDCAN_RXF0S_F0F) { + 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 } @@ -195,6 +195,9 @@ void can_rx(uint8_t can_number) { // 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; @@ -226,7 +229,7 @@ void can_rx(uint8_t can_number) { } // update read index - CANx->RXF0A = rx_fifo_idx; + FDCANx->RXF0A = rx_fifo_idx; } // Error handling @@ -255,9 +258,9 @@ bool can_init(uint8_t can_number) { REGISTER_INTERRUPT(FDCAN3_IT1_IRQn, FDCAN3_IT1_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3) if (can_number != 0xffU) { - FDCAN_GlobalTypeDef *CANx = CANIF_FROM_CAN_NUM(can_number); + FDCAN_GlobalTypeDef *FDCANx = CANIF_FROM_CAN_NUM(can_number); ret &= can_set_speed(can_number); - ret &= llcan_init(CANx); + ret &= llcan_init(FDCANx); // in case there are queued up messages process_can(can_number); } diff --git a/board/drivers/gmlan_alt.h b/board/drivers/gmlan_alt.h index 53f46c2e02..45378747df 100644 --- a/board/drivers/gmlan_alt.h +++ b/board/drivers/gmlan_alt.h @@ -128,9 +128,9 @@ void setup_timer(void) { REGISTER_INTERRUPT(TIM8_BRK_TIM12_IRQn, TIM12_IRQ_Handler, 40000U, FAULT_INTERRUPT_RATE_GMLAN) // setup - register_set(&(TIM12->PSC), (48-1), 0xFFFFU); // Tick on 1 us + 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), (30-1), 0xFFFFU); // 33.3 kbps + register_set(&(TIM12->ARR), (30U-1U), 0xFFFFU); // 33.3 kbps // in case it's disabled NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn); diff --git a/board/drivers/gpio.h b/board/drivers/gpio.h index 77af87658c..fe3194f3ba 100644 --- a/board/drivers/gpio.h +++ b/board/drivers/gpio.h @@ -10,6 +10,11 @@ #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; @@ -63,6 +68,18 @@ int get_gpio_input(GPIO_TypeDef *GPIO, unsigned int pin) { return (GPIO->IDR & (1U << pin)) == (1U << 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) { diff --git a/board/drivers/harness.h b/board/drivers/harness.h index 6bf3fe1b9c..36f73f2573 100644 --- a/board/drivers/harness.h +++ b/board/drivers/harness.h @@ -25,7 +25,8 @@ struct harness_configuration { uint8_t adc_channel_SBU2; }; -void set_intercept_relay(bool intercept) { +// The ignition relay is only used for testing purposes +void set_intercept_relay(bool intercept, bool ignition_relay) { if (current_board->harness_config->has_harness) { bool drive_relay = intercept; if (harness.status == HARNESS_STATUS_NC) { @@ -33,7 +34,7 @@ void set_intercept_relay(bool intercept) { drive_relay = false; } - if (drive_relay) { + if (drive_relay || ignition_relay) { harness.relay_driven = true; } @@ -41,14 +42,14 @@ void set_intercept_relay(bool intercept) { while (harness.sbu_adc_lock == true) {} if (harness.status == HARNESS_STATUS_NORMAL) { - set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, true); + 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, true); + set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !ignition_relay); } - if (!drive_relay) { + if (!(drive_relay || ignition_relay)) { harness.relay_driven = false; } } @@ -129,5 +130,5 @@ void harness_init(void) { } // keep buses connected by default - set_intercept_relay(false); + set_intercept_relay(false, false); } diff --git a/board/drivers/spi.h b/board/drivers/spi.h index af9dd8a574..6d0beda9ad 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -10,8 +10,9 @@ #ifdef STM32H7 #define SPI_BUF_SIZE 2048U -__attribute__((section(".ram_d1"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; -__attribute__((section(".ram_d2"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; +// 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]; diff --git a/board/drivers/uart.h b/board/drivers/uart.h index 824e7d7592..cde8137402 100644 --- a/board/drivers/uart.h +++ b/board/drivers/uart.h @@ -1,8 +1,7 @@ -// IRQs: USART1, USART2, USART3, UART5 +// IRQs: USART2, USART3, UART5 // ***************************** Definitions ***************************** #define FIFO_SIZE_INT 0x400U -#define FIFO_SIZE_DMA 0x1000U typedef struct uart_ring { volatile uint16_t w_ptr_tx; @@ -15,11 +14,10 @@ typedef struct uart_ring { uint32_t rx_fifo_size; USART_TypeDef *uart; void (*callback)(struct uart_ring*); - bool dma_rx; bool overwrite; } uart_ring; -#define UART_BUFFER(x, size_rx, size_tx, uart_ptr, callback_ptr, rx_dma, overwrite_mode) \ +#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 = { \ @@ -33,7 +31,6 @@ typedef struct uart_ring { .rx_fifo_size = (size_rx), \ .uart = (uart_ptr), \ .callback = (callback_ptr), \ - .dma_rx = (rx_dma), \ .overwrite = (overwrite_mode) \ }; @@ -44,23 +41,20 @@ void uart_send_break(uart_ring *u); // ******************************** UART buffers ******************************** -// gps = USART1 -UART_BUFFER(gps, FIFO_SIZE_DMA, FIFO_SIZE_INT, USART1, NULL, true, false) - // lin1, K-LINE = UART5 // lin2, L-LINE = USART3 -UART_BUFFER(lin1, FIFO_SIZE_INT, FIFO_SIZE_INT, UART5, NULL, false, false) -UART_BUFFER(lin2, FIFO_SIZE_INT, FIFO_SIZE_INT, USART3, NULL, false, false) +UART_BUFFER(lin1, FIFO_SIZE_INT, FIFO_SIZE_INT, UART5, NULL, false) +UART_BUFFER(lin2, FIFO_SIZE_INT, FIFO_SIZE_INT, USART3, NULL, false) // debug = USART2 -UART_BUFFER(debug, FIFO_SIZE_INT, FIFO_SIZE_INT, USART2, debug_ring_callback, false, true) +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, false, true) + 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, false, true) + UART_BUFFER(som_debug, 1U, 1U, NULL, NULL, true) #endif uart_ring *get_ring_by_number(int a) { @@ -69,9 +63,6 @@ uart_ring *get_ring_by_number(int a) { case 0: ring = &uart_ring_debug; break; - case 1: - ring = &uart_ring_gps; - break; case 2: ring = &uart_ring_lin1; break; diff --git a/board/drivers/usb.h b/board/drivers/usb.h index d10bed15e3..c291d7e64c 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -198,8 +198,13 @@ uint16_t string_product_desc[] = { // default serial number when we're not a panda uint16_t string_serial_desc[] = { +#ifdef PEDAL + STRING_DESCRIPTOR_HEADER(5), + 'p', 'e', 'd', 'a', 'l' +#else STRING_DESCRIPTOR_HEADER(4), 'n', 'o', 'n', 'e' +#endif }; // a string containing the default configuration index diff --git a/board/early_init.h b/board/early_init.h index 6c60b20fbb..ae652aebce 100644 --- a/board/early_init.h +++ b/board/early_init.h @@ -51,9 +51,9 @@ void early_initialization(void) { detect_board_type(); if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) { - #ifdef PANDA - current_board->set_gps_mode(GPS_DISABLED); - #endif + #ifdef PANDA + current_board->init_bootloader(); + #endif current_board->set_led(LED_GREEN, 1); jump_to_bootloader(); } diff --git a/board/flasher.h b/board/flasher.h index 351a899915..7a124cfd3c 100644 --- a/board/flasher.h +++ b/board/flasher.h @@ -148,14 +148,14 @@ int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) { #ifdef PEDAL #include "stm32fx/llbxcan.h" -#define CAN CAN1 +#define CANx CAN1 #define CAN_BL_INPUT 0x1 #define CAN_BL_OUTPUT 0x2 void CAN1_TX_IRQ_Handler(void) { // clear interrupt - CAN->TSR |= CAN_TSR_RQCP0; + CANx->TSR |= CAN_TSR_RQCP0; } #define ISOTP_BUF_SIZE 0x110 @@ -171,21 +171,21 @@ int isotp_buf_out_idx = 0; void bl_can_send(uint8_t *odat) { // wait for send - while (!(CAN->TSR & CAN_TSR_TME0)); + while (!(CANx->TSR & CAN_TSR_TME0)); // send continue - CAN->sTxMailBox[0].TDLR = ((uint32_t*)odat)[0]; - CAN->sTxMailBox[0].TDHR = ((uint32_t*)odat)[1]; - CAN->sTxMailBox[0].TDTR = 8; - CAN->sTxMailBox[0].TIR = (CAN_BL_OUTPUT << 21) | 1; + CANx->sTxMailBox[0].TDLR = ((uint32_t*)odat)[0]; + CANx->sTxMailBox[0].TDHR = ((uint32_t*)odat)[1]; + CANx->sTxMailBox[0].TDTR = 8; + CANx->sTxMailBox[0].TIR = (CAN_BL_OUTPUT << 21) | 1; } void CAN1_RX0_IRQ_Handler(void) { - while (CAN->RF0R & CAN_RF0R_FMP0) { - if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { + while (CANx->RF0R & CAN_RF0R_FMP0) { + if ((CANx->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { uint8_t dat[8]; for (int i = 0; i < 8; i++) { - dat[i] = GET_MAILBOX_BYTE(&CAN->sFIFOMailBox[0], i); + dat[i] = GET_MAILBOX_BYTE(&CANx->sFIFOMailBox[0], i); } uint8_t odat[8]; uint8_t type = dat[0] & 0xF0; @@ -193,7 +193,7 @@ void CAN1_RX0_IRQ_Handler(void) { // continue while (isotp_buf_out_remain > 0) { // wait for send - while (!(CAN->TSR & CAN_TSR_TME0)); + while (!(CANx->TSR & CAN_TSR_TME0)); odat[0] = 0x20 | isotp_buf_out_idx; memcpy(odat+1, isotp_buf_out_ptr, 7); @@ -251,12 +251,12 @@ void CAN1_RX0_IRQ_Handler(void) { } } // next - CAN->RF0R |= CAN_RF0R_RFOM0; + CANx->RF0R |= CAN_RF0R_RFOM0; } } void CAN1_SCE_IRQ_Handler(void) { - llcan_clear_send(CAN); + llcan_clear_send(CANx); } #endif @@ -284,8 +284,8 @@ void soft_flasher_start(void) { current_board->enable_can_transceiver(1, true); // init can - llcan_set_speed(CAN1, 5000, false, false); - llcan_init(CAN1); + llcan_set_speed(CANx, 5000, false, false); + llcan_init(CANx); #endif gpio_usart2_init(); diff --git a/board/jungle/README.md b/board/jungle/README.md new file mode 100644 index 0000000000..4c271cbf21 --- /dev/null +++ b/board/jungle/README.md @@ -0,0 +1,26 @@ +Welcome to the jungle +====== + +Firmware for the Panda Jungle testing board. +Available for purchase at the [comma shop](https://comma.ai/shop/panda-jungle). + +## udev rules + +To make the jungle usable without root permissions, you might need to setup udev rules for it. +On ubuntu, this should do the trick: +``` bash +sudo tee /etc/udev/rules.d/12-panda_jungle.rules < bool: + dfu_serial = self.get_dfu_serial() + + if reset: + self.reset(enter_bootstub=True) + self.reset(enter_bootloader=True) + + if not self.wait_for_dfu(dfu_serial, timeout=timeout): + return False + + dfu = PandaJungleDFU(dfu_serial) + dfu.recover() + + # reflash after recover + self.connect(True, True) + self.flash() + return True + + def get_mcu_type(self) -> McuType: + hw_type = self.get_type() + if hw_type in PandaJungle.F4_DEVICES: + return McuType.F4 + elif hw_type in PandaJungle.H7_DEVICES: + return McuType.H7 + raise ValueError(f"unknown HW type: {hw_type}") + + def up_to_date(self) -> bool: + current = self.get_signature() + fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn.replace("panda", "panda_jungle")) + expected = Panda.get_signature_from_firmware(fn) + return (current == expected) + + # ******************* health ******************* + + @ensure_jungle_health_packet_version + def health(self): + dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xd2, 0, 0, self.HEALTH_STRUCT.size) + a = self.HEALTH_STRUCT.unpack(dat) + return { + "uptime": a[0], + "ch1_power": a[1], + "ch2_power": a[2], + "ch3_power": a[3], + "ch4_power": a[4], + "ch5_power": a[5], + "ch6_power": a[6], + "ch1_sbu1_voltage": a[7] / 1000.0, + "ch1_sbu2_voltage": a[8] / 1000.0, + "ch2_sbu1_voltage": a[9] / 1000.0, + "ch2_sbu2_voltage": a[10] / 1000.0, + "ch3_sbu1_voltage": a[11] / 1000.0, + "ch3_sbu2_voltage": a[12] / 1000.0, + "ch4_sbu1_voltage": a[13] / 1000.0, + "ch4_sbu2_voltage": a[14] / 1000.0, + "ch5_sbu1_voltage": a[15] / 1000.0, + "ch5_sbu2_voltage": a[16] / 1000.0, + "ch6_sbu1_voltage": a[17] / 1000.0, + "ch6_sbu2_voltage": a[18] / 1000.0, + } + + # ******************* control ******************* + + # Returns tuple with health packet version and CAN packet/USB packet version + def get_packets_versions(self): + dat = self._handle.controlRead(PandaJungle.REQUEST_IN, 0xdd, 0, 0, 3) + if dat and len(dat) == 3: + a = struct.unpack("BBB", dat) + return (a[0], a[1], a[2]) + return (-1, -1, -1) + + # ******************* jungle stuff ******************* + + def set_panda_power(self, enabled): + self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa0, int(enabled), 0, b'') + + def set_harness_orientation(self, mode): + self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa1, int(mode), 0, b'') + + def set_ignition(self, enabled): + self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xa2, int(enabled), 0, b'') + + def set_can_silent(self, silent): + self._handle.controlWrite(PandaJungle.REQUEST_OUT, 0xf5, int(silent), 0, b'') + + # ******************* serial ******************* + + def debug_read(self): + ret = [] + while 1: + lret = bytes(self._handle.controlRead(PandaJungle.REQUEST_IN, 0xe0, 0, 0, 0x40)) + if len(lret) == 0: + break + ret.append(lret) + return b''.join(ret) + + # ******************* header pins ******************* + + def set_header_pin(self, pin_num, enabled): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xf7, int(pin_num), int(enabled), b'') diff --git a/board/jungle/boards/board_declarations.h b/board/jungle/boards/board_declarations.h new file mode 100644 index 0000000000..3fe4c2fab9 --- /dev/null +++ b/board/jungle/boards/board_declarations.h @@ -0,0 +1,77 @@ +// ******************** 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_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 char *board_type; + 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_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; + bool has_hw_gmlan; +}; + +// ******************* 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); +} diff --git a/board/jungle/boards/board_v1.h b/board/jungle/boards/board_v1.h new file mode 100644 index 0000000000..8d41b94524 --- /dev/null +++ b/board/jungle/boards/board_v1.h @@ -0,0 +1,175 @@ + +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 = { + .board_type = "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_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/board/jungle/boards/board_v2.h b/board/jungle/boards/board_v2.h new file mode 100644 index 0000000000..0a6e27368c --- /dev/null +++ b/board/jungle/boards/board_v2.h @@ -0,0 +1,308 @@ + +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; +void board_v2_set_panda_power(bool enable) { + panda_power = enable; + gpio_set_all_output(power_pins, sizeof(power_pins) / sizeof(gpio_t), enable); +} + +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 = { + .board_type = "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_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/board/jungle/flash.py b/board/jungle/flash.py new file mode 100755 index 0000000000..75a7f0c8ee --- /dev/null +++ b/board/jungle/flash.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import os +import subprocess + +from panda import PandaJungle + +board_path = os.path.dirname(os.path.realpath(__file__)) + +if __name__ == "__main__": + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) + + serials = PandaJungle.list() + print(f"found {len(serials)} panda jungle(s) - {serials}") + for s in serials: + print("flashing", s) + with PandaJungle(serial=s) as p: + p.flash() diff --git a/board/jungle/jungle_health.h b/board/jungle/jungle_health.h new file mode 100644 index 0000000000..931ed3715e --- /dev/null +++ b/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/board/jungle/main.c b/board/jungle/main.c new file mode 100644 index 0000000000..43193a51aa --- /dev/null +++ b/board/jungle/main.c @@ -0,0 +1,219 @@ +// ********************* Includes ********************* +#include "board/config.h" + +#include "board/safety.h" +#include "board/drivers/gmlan_alt.h" + +#include "board/drivers/pwm.h" +#include "board/drivers/usb.h" + +#include "board/early_init.h" +#include "board/provision.h" + +#include "board/health.h" +#include "jungle_health.h" + +#include "board/drivers/can_common.h" + +#ifdef STM32H7 + #include "board/drivers/fdcan.h" +#else + #include "board/drivers/bxcan.h" +#endif + +#include "board/obj/gitversion.h" + +#include "board/can_comms.h" +#include "main_comms.h" + + +// ********************* Serial debugging ********************* + +void debug_ring_callback(uart_ring *ring) { + char rcv; + while (getc(ring, &rcv)) { + (void)injectc(ring, rcv); + } +} + +// ***************************** main code ***************************** + +// cppcheck-suppress unusedFunction ; used in headers not included in cppcheck +void __initialize_hardware_early(void) { + early_initialization(); +} + +void __attribute__ ((noinline)) enable_fpu(void) { + // enable the FPU + SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U))); +} + +// called at 8Hz +uint32_t loop_counter = 0U; +uint16_t button_press_cnt = 0U; +void tick_handler(void) { + if (TICK_TIMER->SR != 0) { + // tick drivers at 8Hz + usb_tick(); + + // decimated to 1Hz + if ((loop_counter % 8) == 0U) { + #ifdef DEBUG + print("** blink "); + print("rx:"); puth4(can_rx_q.r_ptr); print("-"); puth4(can_rx_q.w_ptr); print(" "); + print("tx1:"); puth4(can_tx1_q.r_ptr); print("-"); puth4(can_tx1_q.w_ptr); print(" "); + print("tx2:"); puth4(can_tx2_q.r_ptr); print("-"); puth4(can_tx2_q.w_ptr); print(" "); + print("tx3:"); puth4(can_tx3_q.r_ptr); print("-"); puth4(can_tx3_q.w_ptr); print("\n"); + #endif + + current_board->board_tick(); + + // check registers + check_registers(); + + // turn off the blue LED, turned on by CAN + current_board->set_led(LED_BLUE, false); + + // Blink and OBD CAN +#ifdef FINAL_PROVISIONING + current_board->set_can_mode(can_mode == CAN_MODE_NORMAL ? CAN_MODE_OBD_CAN2 : CAN_MODE_NORMAL); +#endif + + // on to the next one + uptime_cnt += 1U; + } + + current_board->set_led(LED_GREEN, green_led_enabled); + + // Check on button + bool current_button_status = current_board->get_button(); + + if (current_button_status && button_press_cnt == 10) { + current_board->set_panda_power(!panda_power); + } + +#ifdef FINAL_PROVISIONING + // Reset CAN core if it got stuck in bus off state after shorted CANH/L + // Looks like an issue with H7 that it doesn't want to recover from bus off + // FIXME: this must be fixed on the driver level, temporary workaround + for (uint8_t i = 0U; i < 3U; i++) { + update_can_health_pkt(i, 0); + if (can_health[i].bus_off == 1U) { + can_init(i); + } + } + // Ignition blinking + uint8_t ignition_bitmask = 0U; + for (uint8_t i = 0U; i < 6U; i++) { + ignition_bitmask |= ((loop_counter % 12U) < ((uint32_t) i + 2U)) << i; + } + current_board->set_individual_ignition(ignition_bitmask); + + // SBU voltage reporting + if (current_board->has_sbu_sense) { + for (uint8_t i = 0U; i < 6U; i++) { + CANPacket_t pkt = { 0 }; + pkt.data_len_code = 8U; + pkt.addr = 0x100U + i; + *(uint16_t *) &pkt.data[0] = current_board->get_sbu_mV(i + 1U, SBU1); + *(uint16_t *) &pkt.data[2] = current_board->get_sbu_mV(i + 1U, SBU2); + pkt.data[4] = (ignition_bitmask >> i) & 1U; + can_set_checksum(&pkt); + can_send(&pkt, 0U, false); + } + } +#else + // toggle ignition on button press + static bool prev_button_status = false; + if (!current_button_status && prev_button_status && button_press_cnt < 10){ + current_board->set_ignition(!ignition); + } + prev_button_status = current_button_status; +#endif + + button_press_cnt = current_button_status ? button_press_cnt + 1 : 0; + + loop_counter++; + } + TICK_TIMER->SR = 0; +} + + +int main(void) { + // Init interrupt table + init_interrupts(true); + + // shouldn't have interrupts here, but just in case + disable_interrupts(); + + // init early devices + clock_init(); + peripherals_init(); + detect_board_type(); + + // print hello + print("\n\n\n************************ MAIN START ************************\n"); + + // check for non-supported board types + if (hw_type == HW_TYPE_UNKNOWN) { + print("Unsupported board type\n"); + while (1) { /* hang */ } + } + + print("Config:\n"); + print(" Board type: "); print(current_board->board_type); print("\n"); + + // init board + current_board->init(); + + // we have an FPU, let's use it! + enable_fpu(); + + microsecond_timer_init(); + + // 8Hz timer + REGISTER_INTERRUPT(TICK_TIMER_IRQ, tick_handler, 10U, FAULT_INTERRUPT_RATE_TICK) + tick_timer_init(); + +#ifdef DEBUG + print("DEBUG ENABLED\n"); +#endif + // enable USB (right before interrupts or enum can fail!) + usb_init(); + + print("**** INTERRUPTS ON ****\n"); + enable_interrupts(); + + can_silent = ALL_CAN_LIVE; + set_safety_hooks(SAFETY_ALLOUTPUT, 0U); + + can_init_all(); + current_board->set_harness_orientation(HARNESS_ORIENTATION_1); + +#ifdef FINAL_PROVISIONING + print("---- FINAL PROVISIONING BUILD ---- \n"); + can_set_forwarding(0, 2); + can_set_forwarding(1, 2); +#endif + + // LED should keep on blinking all the time + uint64_t cnt = 0; + for (cnt=0;;cnt++) { + // useful for debugging, fade breaks = panda is overloaded + for (uint32_t fade = 0U; fade < MAX_LED_FADE; fade += 1U) { + current_board->set_led(LED_RED, true); + delay(fade >> 4); + current_board->set_led(LED_RED, false); + delay((MAX_LED_FADE - fade) >> 4); + } + + for (uint32_t fade = MAX_LED_FADE; fade > 0U; fade -= 1U) { + current_board->set_led(LED_RED, true); + delay(fade >> 4); + current_board->set_led(LED_RED, false); + delay((MAX_LED_FADE - fade) >> 4); + } + } + + return 0; +} diff --git a/board/jungle/main_comms.h b/board/jungle/main_comms.h new file mode 100644 index 0000000000..890286028e --- /dev/null +++ b/board/jungle/main_comms.h @@ -0,0 +1,257 @@ +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(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; + // **** 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)) && getc(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/board/jungle/recover.py b/board/jungle/recover.py new file mode 100755 index 0000000000..98afb06748 --- /dev/null +++ b/board/jungle/recover.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +import os +import time +import subprocess + +from panda import PandaJungle, PandaJungleDFU + +board_path = os.path.dirname(os.path.realpath(__file__)) + +if __name__ == "__main__": + subprocess.check_call(f"scons -C {board_path}/.. -u -j$(nproc) {board_path}", shell=True) + + for s in PandaJungle.list(): + print("putting", s, "in DFU mode") + with PandaJungle(serial=s) as p: + p.reset(enter_bootstub=True) + p.reset(enter_bootloader=True) + + # wait for reset panda jungles to come back up + time.sleep(1) + + dfu_serials = PandaJungleDFU.list() + print(f"found {len(dfu_serials)} panda jungle(s) in DFU - {dfu_serials}") + for s in dfu_serials: + print("flashing", s) + PandaJungleDFU(s).recover() diff --git a/board/jungle/scripts/can_health.py b/board/jungle/scripts/can_health.py new file mode 100755 index 0000000000..ff068b5baa --- /dev/null +++ b/board/jungle/scripts/can_health.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +import time +from panda import PandaJungle + +if __name__ == "__main__": + jungle = PandaJungle() + + while True: + for bus in range(3): + print(bus, jungle.can_health(bus)) + print() + time.sleep(1) diff --git a/board/jungle/scripts/can_printer.py b/board/jungle/scripts/can_printer.py new file mode 100755 index 0000000000..675fc508a1 --- /dev/null +++ b/board/jungle/scripts/can_printer.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +import os +import time +from collections import defaultdict +import binascii + +from panda import PandaJungle + +# fake +def sec_since_boot(): + return time.time() + +def can_printer(): + p = PandaJungle() + + 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/board/jungle/scripts/debug_console.py b/board/jungle/scripts/debug_console.py new file mode 100755 index 0000000000..f35388796f --- /dev/null +++ b/board/jungle/scripts/debug_console.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +import os +import sys +import time + +from panda import PandaJungle + +setcolor = ["\033[1;32;40m", "\033[1;31;40m"] +unsetcolor = "\033[00m" + +if __name__ == "__main__": + while True: + try: + claim = os.getenv("CLAIM") is not None + + serials = PandaJungle.list() + if os.getenv("SERIAL"): + serials = [x for x in serials if x==os.getenv("SERIAL")] + + panda_jungles = [PandaJungle(x, claim=claim) for x in serials] + + if not len(panda_jungles): + sys.exit("no panda jungles found") + + while True: + for i, panda_jungle in enumerate(panda_jungles): + while True: + ret = panda_jungle.debug_read() + if len(ret) > 0: + sys.stdout.write(setcolor[i] + ret.decode('ascii') + unsetcolor) + sys.stdout.flush() + else: + break + time.sleep(0.01) + except Exception as e: + print(e) + print("panda jungle disconnected!") + time.sleep(0.5) diff --git a/board/jungle/scripts/echo_loopback_test.py b/board/jungle/scripts/echo_loopback_test.py new file mode 100755 index 0000000000..78b65b5341 --- /dev/null +++ b/board/jungle/scripts/echo_loopback_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +import os +import time +import contextlib +import random +from termcolor import cprint + +from panda import PandaJungle + +# This script is intended to be used in conjunction with the echo.py test script from panda. +# It sends messages on bus 0, 1, 2 and checks for a reversed response to be sent back. + +################################################################# +############################# UTILS ############################# +################################################################# +def print_colored(text, color): + cprint(text + " "*40, color, end="\r") + +def get_test_string(): + return b"test"+os.urandom(4) + +################################################################# +############################# TEST ############################## +################################################################# + +def test_loopback(): + for bus in range(3): + # Clear can + jungle.can_clear(bus) + # Send a random message + address = random.randint(1, 2000) + data = get_test_string() + jungle.can_send(address, data, bus) + time.sleep(0.1) + + # Make sure it comes back reversed + incoming = jungle.can_recv() + found = False + for message in incoming: + incomingAddress, _, incomingData, incomingBus = message + if incomingAddress == address and incomingData == data[::-1] and incomingBus == bus: + found = True + break + if not found: + cprint("\nFAILED", "red") + raise AssertionError + +################################################################# +############################# MAIN ############################## +################################################################# +jungle = None +counter = 0 + +if __name__ == "__main__": + # Connect to jungle silently + print_colored("Connecting to jungle", "blue") + with open(os.devnull, "w") as devnull: + with contextlib.redirect_stdout(devnull): + jungle = PandaJungle() + jungle.set_panda_power(True) + jungle.set_ignition(False) + + # Run test + while True: + jungle.can_clear(0xFFFF) + test_loopback() + counter += 1 + print_colored(str(counter) + " loopback cycles complete", "blue") diff --git a/board/jungle/scripts/get_version.py b/board/jungle/scripts/get_version.py new file mode 100755 index 0000000000..ad4a1c4264 --- /dev/null +++ b/board/jungle/scripts/get_version.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +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())) + + diff --git a/board/jungle/scripts/health_test.py b/board/jungle/scripts/health_test.py new file mode 100755 index 0000000000..039f840e0b --- /dev/null +++ b/board/jungle/scripts/health_test.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +import time +from pprint import pprint + +from panda import PandaJungle + +if __name__ == "__main__": + i = 0 + pi = 0 + + pj = PandaJungle() + while True: + st = time.monotonic() + while time.monotonic() - st < 1: + pj.health() + pj.can_health(0) + i += 1 + pprint(pj.health()) + print(f"Speed: {i - pi}Hz") + pi = i + diff --git a/board/jungle/scripts/loopback_test.py b/board/jungle/scripts/loopback_test.py new file mode 100755 index 0000000000..10caf42cc4 --- /dev/null +++ b/board/jungle/scripts/loopback_test.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +import os +import time +import contextlib +import random +from termcolor import cprint + +from panda import Panda, PandaJungle + +NUM_PANDAS_PER_TEST = 1 +FOR_RELEASE_BUILDS = os.getenv("RELEASE") is not None # Release builds do not have ALLOUTPUT mode + +BUS_SPEEDS = [125, 500, 1000] + +################################################################# +############################# UTILS ############################# +################################################################# +# To suppress the connection text +def silent_panda_connect(serial): + with open(os.devnull, "w") as devnull: + with contextlib.redirect_stdout(devnull): + panda = Panda(serial) + return panda + +def print_colored(text, color): + cprint(text + " "*40, color, end="\r") + +def connect_to_pandas(): + print_colored("Connecting to pandas", "blue") + # Connect to pandas + pandas = [] + for serial in panda_serials: + pandas.append(silent_panda_connect(serial)) + print_colored("Connected", "blue") + +def start_with_orientation(orientation): + print_colored("Restarting pandas with orientation " + str(orientation), "blue") + jungle.set_panda_power(False) + jungle.set_harness_orientation(orientation) + time.sleep(4) + jungle.set_panda_power(True) + time.sleep(2) + connect_to_pandas() + +def can_loopback(sender): + receivers = list(filter((lambda p: (p != sender)), [jungle] + pandas)) + + for bus in range(4): + obd = False + if bus == 3: + obd = True + bus = 1 + + # Clear buses + for receiver in receivers: + receiver.set_obd(obd) + receiver.can_clear(bus) # TX + receiver.can_clear(0xFFFF) # RX + + # Send a random string + addr = 0x18DB33F1 if FOR_RELEASE_BUILDS else random.randint(1, 2000) + string = b"test"+os.urandom(4) + sender.set_obd(obd) + time.sleep(0.2) + sender.can_send(addr, string, bus) + time.sleep(0.2) + + # Check if all receivers have indeed received them in their receiving buffers + for receiver in receivers: + content = receiver.can_recv() + + # Check amount of messages + if len(content) != 1: + raise Exception("Amount of received CAN messages (" + str(len(content)) + ") does not equal 1. Bus: " + str(bus) +" OBD: " + str(obd)) + + # Check content + if content[0][0] != addr or content[0][2] != string: + raise Exception("Received CAN message content or address does not match") + + # Check bus + if content[0][3] != bus: + raise Exception("Received CAN message bus does not match") + +################################################################# +############################# TEST ############################## +################################################################# + +def test_loopback(): + # disable safety modes + for panda in pandas: + panda.set_safety_mode(Panda.SAFETY_ELM327 if FOR_RELEASE_BUILDS else Panda.SAFETY_ALLOUTPUT) + + # perform loopback with jungle as a sender + can_loopback(jungle) + + # perform loopback with each possible panda as a sender + for panda in pandas: + can_loopback(panda) + + # enable safety modes + for panda in pandas: + panda.set_safety_mode(Panda.SAFETY_SILENT) + +################################################################# +############################# MAIN ############################## +################################################################# +jungle = None +pandas = [] # type: ignore +panda_serials = [] +counter = 0 + +if __name__ == "__main__": + # Connect to jungle silently + print_colored("Connecting to jungle", "blue") + with open(os.devnull, "w") as devnull: + with contextlib.redirect_stdout(devnull): + jungle = PandaJungle() + jungle.set_panda_power(True) + jungle.set_ignition(False) + + # Connect to new pandas before starting tests + print_colored("Waiting for " + str(NUM_PANDAS_PER_TEST) + " pandas to be connected", "yellow") + while True: + connected_serials = Panda.list() + if len(connected_serials) == NUM_PANDAS_PER_TEST: + panda_serials = connected_serials + break + + start_with_orientation(PandaJungle.HARNESS_ORIENTATION_1) + + # Set bus speeds + for device in pandas + [jungle]: + for bus in range(len(BUS_SPEEDS)): + device.set_can_speed_kbps(bus, BUS_SPEEDS[bus]) + + # Run test + while True: + test_loopback() + counter += 1 + print_colored(str(counter) + " loopback cycles complete", "blue") diff --git a/board/jungle/scripts/panda_identification_test.py b/board/jungle/scripts/panda_identification_test.py new file mode 100755 index 0000000000..a61b0d608b --- /dev/null +++ b/board/jungle/scripts/panda_identification_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import os +import time +import random +import contextlib + +from panda import PandaJungle +from panda import Panda + +PANDA_UNDER_TEST = Panda.HW_TYPE_UNO + +panda_jungle = PandaJungle() + +def silent_panda_connect(): + with open(os.devnull, "w") as devnull: + with contextlib.redirect_stdout(devnull): + panda = Panda() + return panda + +def reboot_panda(harness_orientation=PandaJungle.HARNESS_ORIENTATION_NONE, ignition=False): + print(f"Restarting panda with harness orientation: {harness_orientation} and ignition: {ignition}") + panda_jungle.set_panda_power(False) + panda_jungle.set_harness_orientation(harness_orientation) + panda_jungle.set_ignition(ignition) + time.sleep(2) + panda_jungle.set_panda_power(True) + time.sleep(2) + +count = 0 +if __name__ == "__main__": + while True: + ignition = random.randint(0, 1) + harness_orientation = random.randint(0, 2) + reboot_panda(harness_orientation, ignition) + + p = silent_panda_connect() + assert p.get_type() == PANDA_UNDER_TEST + assert p.health()['car_harness_status'] == harness_orientation + if harness_orientation != PandaJungle.HARNESS_ORIENTATION_NONE: + assert p.health()['ignition_line'] == ignition + + count += 1 + print(f"Passed {count} loops") + + diff --git a/board/jungle/scripts/start.py b/board/jungle/scripts/start.py new file mode 100755 index 0000000000..76afd14ac4 --- /dev/null +++ b/board/jungle/scripts/start.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import sys + +from panda import PandaJungle + +if __name__ == "__main__": + jungle = PandaJungle() + + # If first argument specified, it sets the ignition (0 or 1) + if len(sys.argv) > 1: + if sys.argv[1] == '1': + jungle.set_ignition(True) + else: + jungle.set_ignition(False) + jungle.set_harness_orientation(1) + jungle.set_panda_power(True) + + diff --git a/board/jungle/stm32fx/board.h b/board/jungle/stm32fx/board.h new file mode 100644 index 0000000000..0adf7923b2 --- /dev/null +++ b/board/jungle/stm32fx/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/board/jungle/stm32h7/board.h b/board/jungle/stm32h7/board.h new file mode 100644 index 0000000000..4fe4fa4637 --- /dev/null +++ b/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/board/jungle/stm32h7/lladc.h b/board/jungle/stm32h7/lladc.h new file mode 100644 index 0000000000..06b742dd38 --- /dev/null +++ b/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/board/main.c b/board/main.c index 1efa7ff4c0..aec9ff0132 100644 --- a/board/main.c +++ b/board/main.c @@ -82,21 +82,21 @@ void set_safety_mode(uint16_t mode, uint16_t param) { switch (mode_copy) { case SAFETY_SILENT: - set_intercept_relay(false); + set_intercept_relay(false, false); if (current_board->has_obd) { current_board->set_can_mode(CAN_MODE_NORMAL); } can_silent = ALL_CAN_SILENT; break; case SAFETY_NOOUTPUT: - set_intercept_relay(false); + set_intercept_relay(false, false); if (current_board->has_obd) { current_board->set_can_mode(CAN_MODE_NORMAL); } can_silent = ALL_CAN_LIVE; break; case SAFETY_ELM327: - set_intercept_relay(false); + set_intercept_relay(false, false); heartbeat_counter = 0U; heartbeat_lost = false; if (current_board->has_obd) { @@ -109,7 +109,7 @@ void set_safety_mode(uint16_t mode, uint16_t param) { can_silent = ALL_CAN_LIVE; break; default: - set_intercept_relay(true); + set_intercept_relay(true, false); heartbeat_counter = 0U; heartbeat_lost = false; if (current_board->has_obd) { @@ -157,6 +157,7 @@ void tick_handler(void) { // tick drivers at 8Hz fan_tick(); usb_tick(); + harness_tick(); simple_watchdog_kick(); // decimated to 1Hz @@ -185,7 +186,6 @@ void tick_handler(void) { current_board->set_led(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED)); // tick drivers at 1Hz - harness_tick(); logging_tick(); const bool recent_heartbeat = heartbeat_counter == 0U; @@ -238,8 +238,8 @@ void tick_handler(void) { heartbeat_engaged_mismatches += 1U; if (heartbeat_engaged_mismatches >= 3U) { disengageFromBrakes = false; - controls_allowed = 0U; - controls_allowed_long = 0U; + controls_allowed = false; + controls_allowed_long = false; } } else { heartbeat_engaged_mismatches = 0U; @@ -373,13 +373,6 @@ int main(void) { log("main start"); - if (current_board->has_gps) { - uart_init(&uart_ring_gps, 9600); - } else { - // enable ESP uart - uart_init(&uart_ring_gps, 115200); - } - if (current_board->has_lin) { // enable LIN uart_init(&uart_ring_lin1, 10400); diff --git a/board/main_comms.h b/board/main_comms.h index f4adface4b..4fe2b98513 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -183,8 +183,8 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { (void)memcpy(resp, ((uint8_t *)UID_BASE), 12); resp_len = 12; break; + // **** 0xc4: get interrupt call rate case 0xc4: - // **** 0xc4: get interrupt call rate if (req->param1 < NUM_INTERRUPTS) { uint32_t load = interrupts[req->param1].call_rate; resp[0] = (load & 0x000000FFU); @@ -194,6 +194,10 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { resp_len = 4U; } break; + // **** 0xc5: DEBUG: drive relay + case 0xc5: + set_intercept_relay((req->param1 & 0x1U), (req->param1 & 0x2U)); + break; // **** 0xd0: fetch serial (aka the provisioned dongle ID) case 0xd0: // addresses are OTP @@ -259,28 +263,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { case 0xd8: NVIC_SystemReset(); break; - // **** 0xd9: set ESP power - case 0xd9: - if (req->param1 == 1U) { - current_board->set_gps_mode(GPS_ENABLED); - } else if (req->param1 == 2U) { - current_board->set_gps_mode(GPS_BOOTMODE); - } else { - current_board->set_gps_mode(GPS_DISABLED); - } - break; - // **** 0xda: reset ESP, with optional boot mode - case 0xda: - current_board->set_gps_mode(GPS_DISABLED); - delay(1000000); - if (req->param1 == 1U) { - current_board->set_gps_mode(GPS_BOOTMODE); - } else { - current_board->set_gps_mode(GPS_ENABLED); - } - delay(1000000); - current_board->set_gps_mode(GPS_ENABLED); - break; // **** 0xdb: set GMLAN (white/grey) or OBD CAN (black) multiplexing mode case 0xdb: if(current_board->has_obd){ @@ -340,11 +322,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { break; } - // TODO: Remove this again and fix boardd code to hande the message bursts instead of single chars - if (ur == &uart_ring_gps) { - dma_pointer_handler(ur, DMA2_Stream5->NDTR); - } - // read while ((resp_len < MIN(req->length, USBPACKET_MAX_SIZE)) && getc(ur, (char*)&resp[resp_len])) { @@ -397,6 +374,10 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { 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); diff --git a/board/pedal/README b/board/pedal/README.md similarity index 100% rename from board/pedal/README rename to board/pedal/README.md diff --git a/board/pedal/SConscript b/board/pedal/SConscript new file mode 100644 index 0000000000..bfa3c2c19d --- /dev/null +++ b/board/pedal/SConscript @@ -0,0 +1,28 @@ +import copy + +Import('build_project') + +build_projects = {} + +build_projects["pedal"] = { + "MAIN": "main.c", + "BOOTSTUB": "../bootstub.c", + "STARTUP_FILE": "../stm32fx/startup_stm32f205xx.s", + "LINKER_SCRIPT": "../stm32fx/stm32f2_flash.ld", + "APP_START_ADDRESS": "0x8004000", + "PROJECT_FLAGS": [ + "-mcpu=cortex-m3", + "-msoft-float", + "-DSTM32F2", + "-DSTM32F205xx", + "-O2", + "-DPEDAL", + ], +} + +# build with the USB driver enabled +build_projects["pedal_usb"] = copy.deepcopy(build_projects["pedal"]) +build_projects["pedal_usb"]["PROJECT_FLAGS"].append("-DPEDAL_USB") + +for project_name, project in build_projects.items(): + build_project(project_name, project, []) diff --git a/board/pedal/flash_can.sh b/board/pedal/flash_can.sh index 0225bf8dff..b9edf25f12 100755 --- a/board/pedal/flash_can.sh +++ b/board/pedal/flash_can.sh @@ -2,7 +2,7 @@ set -e cd .. -PEDAL=1 scons -u -j$(nproc) +scons -u -j$(nproc) cd pedal -../../tests/pedal/enter_canloader.py ../obj/pedal.bin.signed +../../tests/pedal/enter_canloader.py obj/pedal.bin.signed diff --git a/board/pedal/main.c b/board/pedal/main.c index eb40466106..e0020f1123 100644 --- a/board/pedal/main.c +++ b/board/pedal/main.c @@ -266,7 +266,7 @@ int main(void) { 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) - + // Should run at around 732Hz (see init below) REGISTER_INTERRUPT(TIM3_IRQn, TIM3_IRQ_Handler, 1000U, FAULT_INTERRUPT_RATE_TIM3) @@ -290,12 +290,12 @@ int main(void) { adc_init(); // init can - bool llcan_speed_set = llcan_set_speed(CAN1, 5000, false, false); + bool llcan_speed_set = llcan_set_speed(CAN, 5000, false, false); if (!llcan_speed_set) { print("Failed to set llcan speed"); } - bool ret = llcan_init(CAN1); + bool ret = llcan_init(CAN); UNUSED(ret); // 48mhz / 65536 ~= 732 diff --git a/board/pedal/recover.sh b/board/pedal/recover.sh index 98c5e19862..d7fe0aff5f 100755 --- a/board/pedal/recover.sh +++ b/board/pedal/recover.sh @@ -4,8 +4,8 @@ set -e DFU_UTIL="dfu-util" cd .. -PEDAL=1 scons -u -j$(nproc) +scons -u -j$(nproc) cd pedal -$DFU_UTIL -d 0483:df11 -a 0 -s 0x08004000 -D ../obj/pedal.bin.signed -$DFU_UTIL -d 0483:df11 -a 0 -s 0x08000000:leave -D ../obj/bootstub.pedal.bin +$DFU_UTIL -d 0483:df11 -a 0 -s 0x08004000 -D obj/pedal.bin.signed +$DFU_UTIL -d 0483:df11 -a 0 -s 0x08000000:leave -D obj/bootstub.pedal.bin diff --git a/board/power_saving.h b/board/power_saving.h index 64baad0320..2b7d7c9872 100644 --- a/board/power_saving.h +++ b/board/power_saving.h @@ -13,11 +13,7 @@ void set_power_save_state(int state) { bool enable = false; if (state == POWER_SAVE_STATUS_ENABLED) { print("enable power savings\n"); - if (current_board->has_gps) { - const char UBLOX_SLEEP_MSG[] = "\xb5\x62\x06\x04\x04\x00\x01\x00\x08\x00\x17\x78"; - uart_ring *ur = get_ring_by_number(1); - for (unsigned int i = 0; i < sizeof(UBLOX_SLEEP_MSG) - 1U; i++) while (!putc(ur, UBLOX_SLEEP_MSG[i])); - } + // Disable CAN interrupts if (harness.status == HARNESS_STATUS_FLIPPED) { llcan_irq_disable(cans[0]); @@ -27,11 +23,6 @@ void set_power_save_state(int state) { llcan_irq_disable(cans[1]); } else { print("disable power savings\n"); - if (current_board->has_gps) { - const char UBLOX_WAKE_MSG[] = "\xb5\x62\x06\x04\x04\x00\x01\x00\x09\x00\x18\x7a"; - uart_ring *ur = get_ring_by_number(1); - for (unsigned int i = 0; i < sizeof(UBLOX_WAKE_MSG) - 1U; i++) while (!putc(ur, UBLOX_WAKE_MSG[i])); - } if (harness.status == HARNESS_STATUS_FLIPPED) { llcan_irq_enable(cans[0]); @@ -45,13 +36,6 @@ void set_power_save_state(int state) { current_board->enable_can_transceivers(enable); - // Switch EPS/GPS - if (enable) { - current_board->set_gps_mode(GPS_ENABLED); - } else { - current_board->set_gps_mode(GPS_DISABLED); - } - if(current_board->has_hw_gmlan){ // turn on GMLAN set_gpio_output(GPIOB, 14, enable); diff --git a/board/safety.h b/board/safety.h index 694122f20d..4a4caa456d 100644 --- a/board/safety.h +++ b/board/safety.h @@ -177,8 +177,8 @@ void safety_tick(const addr_checks *rx_checks) { rx_checks->check[i].lagging = lagging; if (lagging) { disengageFromBrakes = false; - controls_allowed = 0; - controls_allowed_long = 0; + controls_allowed = false; + controls_allowed_long = false; } if (lagging || !is_msg_valid(rx_checks->check, i)) { @@ -205,8 +205,8 @@ bool is_msg_valid(AddrCheckStruct addr_list[], int index) { if (!addr_list[index].valid_checksum || !addr_list[index].valid_quality_flag || (addr_list[index].wrong_counters >= MAX_WRONG_COUNTERS)) { valid = false; disengageFromBrakes = false; - controls_allowed = 0; - controls_allowed_long = 0; + controls_allowed = false; + controls_allowed_long = false; } } return valid; @@ -264,8 +264,8 @@ 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)) { disengageFromBrakes = false; - controls_allowed = 0; - controls_allowed_long = 0; + controls_allowed = false; + controls_allowed_long = false; } gas_pressed_prev = gas_pressed; @@ -525,6 +525,12 @@ bool longitudinal_speed_checks(int desired_speed, const LongitudinalLimits limit 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; diff --git a/board/safety/safety_body.h b/board/safety/safety_body.h index 61ebc46dc2..19efab9b7a 100644 --- a/board/safety/safety_body.h +++ b/board/safety/safety_body.h @@ -11,6 +11,9 @@ static int body_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &body_rx_checks, NULL, NULL, NULL, NULL); + // body is never at standstill + vehicle_moving = true; + controls_allowed = valid; return valid; diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index 8c63578208..a0076eb63a 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -44,45 +44,45 @@ typedef struct { // CAN messages for Chrysler/Jeep platforms const ChryslerAddrs CHRYSLER_ADDRS = { - .EPS_2 = 544, // EPS driver input torque - .ESP_1 = 320, // Brake pedal and vehicle speed - .ESP_8 = 284, // Brake pedal and vehicle speed - .ECM_5 = 559, // Throttle position sensor - .DAS_3 = 500, // ACC engagement states from DASM - .DAS_6 = 678, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 658, // LKAS controls from DASM - .LKAS_HEARTBIT = 729, // LKAS HEARTBIT from DASM - .CRUISE_BUTTONS = 571, // Cruise control buttons - .CENTER_STACK_1 = 816, // LKAS Button - .CENTER_STACK_2 = 650, // LKAS Button + .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 + .LKAS_HEARTBIT = 0x2D9, // LKAS HEARTBIT from DASM + .CENTER_STACK_1 = 0x330, // LKAS Button + .CENTER_STACK_2 = 0x28A, // LKAS Button }; // CAN messages for the 5th gen RAM DT platform const ChryslerAddrs CHRYSLER_RAM_DT_ADDRS = { - .EPS_2 = 49, // EPS driver input torque - .ESP_1 = 131, // Brake pedal and vehicle speed - .ESP_8 = 121, // Brake pedal and vehicle speed - .ECM_5 = 157, // Throttle position sensor - .DAS_3 = 153, // ACC engagement states from DASM - .DAS_6 = 250, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 166, // LKAS controls from DASM - .CRUISE_BUTTONS = 177, // Cruise control buttons - .CENTER_STACK_1 = 221, // LKAS Button - .CENTER_STACK_2 = 650, // LKAS Button + .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 + .CENTER_STACK_1 = 0xDD, // LKAS Button + .CENTER_STACK_2 = 0x28A, // LKAS Button }; // CAN messages for the 5th gen RAM HD platform const ChryslerAddrs CHRYSLER_RAM_HD_ADDRS = { - .EPS_2 = 544, // EPS driver input torque - .ESP_1 = 320, // Brake pedal and vehicle speed - .ESP_8 = 284, // Brake pedal and vehicle speed - .ECM_5 = 559, // Throttle position sensor - .DAS_3 = 500, // ACC engagement states from DASM - .DAS_6 = 629, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 630, // LKAS controls from DASM - .CRUISE_BUTTONS = 570, // Cruise control buttons - .CENTER_STACK_1 = 816, // LKAS Button - .CENTER_STACK_2 = 650, // LKAS Button + .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 + .CENTER_STACK_1 = 0x330, // LKAS Button + .CENTER_STACK_2 = 0x28A, // LKAS Button }; const CanMsg CHRYSLER_TX_MSGS[] = { @@ -261,7 +261,9 @@ static int chrysler_tx_hook(CANPacket_t *to_send) { 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; - if (steer_torque_cmd_checks(desired_torque, -1, limits)) { + + bool steer_req = (chrysler_platform == CHRYSLER_PACIFICA) ? (GET_BIT(to_send, 4U) != 0U) : ((GET_BYTE(to_send, 3) & 0x7U) == 2U); + if (steer_torque_cmd_checks(desired_torque, steer_req, limits)) { tx = 0; } } diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index 2f220a10f9..7b06986be0 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -237,7 +237,7 @@ static int ford_rx_hook(CANPacket_t *to_push) { // Signal: Veh_V_ActlEng float filtered_pcm_speed = ((GET_BYTE(to_push, 6) << 8) | GET_BYTE(to_push, 7)) * 0.01 / 3.6; if (ABS(filtered_pcm_speed - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) > FORD_MAX_SPEED_DELTA) { - controls_allowed_long = 0; + controls_allowed_long = false; } } diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index c939dcb05a..346f69ab63 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -27,27 +27,27 @@ const LongitudinalLimits *gm_long_limits; const int GM_STANDSTILL_THRSLD = 10; // 0.311kph -const CanMsg GM_ASCM_TX_MSGS[] = {{384, 0, 4}, {1033, 0, 7}, {1034, 0, 7}, {715, 0, 8}, {880, 0, 6}, // pt bus - {161, 1, 7}, {774, 1, 8}, {776, 1, 7}, {784, 1, 2}, // obs bus - {789, 2, 5}, // ch bus +const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {0x2CB, 0, 8}, {0x370, 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[] = {{384, 0, 4}, // pt bus - {481, 2, 7}, {388, 2, 8}}; // camera bus +const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, // pt bus + {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus -const CanMsg GM_CAM_LONG_TX_MSGS[] = {{384, 0, 4}, {789, 0, 5}, {715, 0, 8}, {880, 0, 6}, // pt bus - {388, 2, 8}}; // camera bus +const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, // pt bus + {0x184, 2, 8}}; // camera bus // TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. AddrCheckStruct gm_addr_checks[] = { - {.msg = {{388, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, - {.msg = {{842, 0, 5, .expected_timestep = 100000U}, { 0 }, { 0 }}}, - {.msg = {{481, 0, 7, .expected_timestep = 100000U}, { 0 }, { 0 }}}, - {.msg = {{190, 0, 6, .expected_timestep = 100000U}, // Volt, Silverado, Acadia Denali - {190, 0, 7, .expected_timestep = 100000U}, // Bolt EUV - {190, 0, 8, .expected_timestep = 100000U}}}, // Escalade - {.msg = {{452, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, - {.msg = {{201, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0x184, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0x34A, 0, 5, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0x1E1, 0, 7, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0xBE, 0, 6, .expected_timestep = 100000U}, // Volt, Silverado, Acadia Denali + {0xBE, 0, 7, .expected_timestep = 100000U}, // Bolt EUV + {0xBE, 0, 8, .expected_timestep = 100000U}}}, // Escalade + {.msg = {{0x1C4, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, + {.msg = {{0xC9, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}}, }; #define GM_RX_CHECK_LEN (sizeof(gm_addr_checks) / sizeof(gm_addr_checks[0])) addr_checks gm_rx_checks = {gm_addr_checks, GM_RX_CHECK_LEN}; @@ -73,7 +73,7 @@ static int gm_rx_hook(CANPacket_t *to_push) { if (valid && (GET_BUS(to_push) == 0U)) { int addr = GET_ADDR(to_push); - if (addr == 388) { + 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 @@ -81,27 +81,27 @@ static int gm_rx_hook(CANPacket_t *to_push) { } // sample rear wheel speeds - if (addr == 842) { + 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 is tied to the PCM) - if ((addr == 481) && !gm_pcm_cruise) { + if ((addr == 0x1E1) && !gm_pcm_cruise) { 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 = 1; - controls_allowed_long = 1; + controls_allowed = true; + controls_allowed_long = true; } // exit controls on cancel press if (button == GM_BTN_CANCEL) { - controls_allowed_long = 0; + controls_allowed_long = false; } cruise_button_prev = button; @@ -109,15 +109,15 @@ static int gm_rx_hook(CANPacket_t *to_push) { // Reference for brake pressed signals: // https://github.com/commaai/openpilot/blob/master/selfdrive/car/gm/carstate.py - if ((addr == 190) && (gm_hw == GM_ASCM)) { + if ((addr == 0xBE) && (gm_hw == GM_ASCM)) { brake_pressed = GET_BYTE(to_push, 1) >= 8U; } - if ((addr == 201) && (gm_hw == GM_CAM)) { + if ((addr == 0xC9) && (gm_hw == GM_CAM)) { brake_pressed = GET_BIT(to_push, 40U) != 0U; } - if (addr == 452) { + if (addr == 0x1C4) { gas_pressed = GET_BYTE(to_push, 5) != 0U; // enter controls on rising edge of ACC, exit controls when ACC off @@ -127,7 +127,7 @@ static int gm_rx_hook(CANPacket_t *to_push) { } } - if (addr == 189) { + if (addr == 0xBD) { regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U; } @@ -136,10 +136,10 @@ static int gm_rx_hook(CANPacket_t *to_push) { mads_acc_main_check(acc_main_on); } - bool stock_ecu_detected = (addr == 384); // ASCMLKASteeringCmd + bool stock_ecu_detected = (addr == 0x180); // ASCMLKASteeringCmd // Check ASCMGasRegenCmd only if we're blocking it - if (!gm_pcm_cruise && (addr == 715)) { + if (!gm_pcm_cruise && (addr == 0x2CB)) { stock_ecu_detected = true; } generic_rx_checks(stock_ecu_detected); @@ -169,7 +169,7 @@ static int gm_tx_hook(CANPacket_t *to_send) { } // BRAKE: safety check - if (addr == 789) { + 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)) { @@ -178,17 +178,19 @@ static int gm_tx_hook(CANPacket_t *to_send) { } // LKA STEER: safety check - if (addr == 384) { + 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); - if (steer_torque_cmd_checks(desired_torque, -1, GM_STEERING_LIMITS)) { + bool steer_req = (GET_BIT(to_send, 3U) != 0U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, GM_STEERING_LIMITS)) { tx = 0; } } // GAS/REGEN: safety check - if (addr == 715) { + if (addr == 0x2CB) { bool apply = GET_BIT(to_send, 0U) != 0U; int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); @@ -203,7 +205,7 @@ static int gm_tx_hook(CANPacket_t *to_send) { } // BUTTONS: used for resume spamming and cruise cancellation with stock longitudinal - if ((addr == 481) && gm_pcm_cruise) { + if ((addr == 0x1E1) && gm_pcm_cruise) { int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; bool allowed_cancel = (button == 6) && cruise_engaged_prev; @@ -223,7 +225,7 @@ static int gm_fwd_hook(int bus_num, int addr) { if (gm_hw == GM_CAM) { if (bus_num == 0) { // block PSCMStatus; forwarded through openpilot to hide an alert from the camera - bool is_pscm_msg = (addr == 388); + bool is_pscm_msg = (addr == 0x184); if (!is_pscm_msg) { bus_fwd = 2; } @@ -231,8 +233,8 @@ static int gm_fwd_hook(int bus_num, int addr) { if (bus_num == 2) { // block lkas message and acc messages if gm_cam_long, forward all others - bool is_lkas_msg = (addr == 384); - bool is_acc_msg = (addr == 789) || (addr == 715) || (addr == 880); + bool is_lkas_msg = (addr == 0x180); + bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); int block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); if (!block_msg) { bus_fwd = 0; diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index 108d00336f..001dea49aa 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -150,28 +150,28 @@ static int honda_rx_hook(CANPacket_t *to_push) { int button = (GET_BYTE(to_push, 0) & 0xE0U) >> 5; int button2 = ((GET_BYTE(to_push, (addr == 0x296) ? 0 : 5) & 0x0CU) >> 2); + // 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 && (set || res)) { + controls_allowed = true; + controls_allowed_long = true; + } + // exit controls once main or cancel are pressed if (button == HONDA_BTN_MAIN) { disengageFromBrakes = false; - controls_allowed = 0; - controls_allowed_long = 0; + controls_allowed = false; + controls_allowed_long = false; } if (button == HONDA_BTN_CANCEL) { - controls_allowed_long = 0; - } - - // enter controls on the falling edge of set or resume - bool set = (button == HONDA_BTN_NONE) && (cruise_button_prev == HONDA_BTN_SET); - bool res = (button == HONDA_BTN_NONE) && (cruise_button_prev == HONDA_BTN_RESUME); - if (acc_main_on && (set || res)) { - controls_allowed = 1; - controls_allowed_long = 1; + controls_allowed_long = false; } cruise_button_prev = button; if ((button2 == 1) && mads_enabled) { - controls_allowed = 1; + controls_allowed = true; } } @@ -231,20 +231,18 @@ static int honda_rx_hook(CANPacket_t *to_push) { int bus_rdr_car = (honda_hw == HONDA_BOSCH) ? 0 : 2; // radar bus, car side bool stock_ecu_detected = false; - if (safety_mode_cnt > RELAY_TRNS_TIMEOUT) { - // 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)) { + // 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); } diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index 419dfaccb7..5d3d5a8d82 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -26,91 +26,91 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = { }; const CanMsg HYUNDAI_TX_MSGS[] = { - {832, 0, 8}, // LKAS11 Bus 0 - {1265, 0, 4}, // CLU11 Bus 0 - {1157, 0, 4}, // LFAHDA_MFC Bus 0 + {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[] = { - {832, 0, 8}, // LKAS11 Bus 0 - {1265, 0, 4}, // CLU11 Bus 0 - {1157, 0, 4}, // LFAHDA_MFC Bus 0 - {1056, 0, 8}, // SCC11 Bus 0 - {1057, 0, 8}, // SCC12 Bus 0 - {1290, 0, 8}, // SCC13 Bus 0 - {905, 0, 8}, // SCC14 Bus 0 - {1186, 0, 2}, // FRT_RADAR11 Bus 0 - {909, 0, 8}, // FCA11 Bus 0 - {1155, 0, 8}, // FCA12 Bus 0 - {2000, 0, 8}, // radar UDS TX addr Bus 0 (for radar disable) + {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[] = { - {832, 0, 8}, // LKAS11 Bus 0 - {1265, 2, 4}, // CLU11 Bus 2 - {1157, 0, 4}, // LFAHDA_MFC Bus 0 + {0x340, 0, 8}, // LKAS11 Bus 0 + {0x4F1, 2, 4}, // CLU11 Bus 2 + {0x485, 0, 4}, // LFAHDA_MFC Bus 0 }; const CanMsg HYUNDAI_CAMERA_SCC_LONG_TX_MSGS[] = { - {832, 0, 8}, // LKAS11 Bus 0 - {1265, 2, 4}, // CLU11 Bus 2 - {1157, 0, 4}, // LFAHDA_MFC Bus 0 - {1056, 0, 8}, // SCC11 Bus 0 - {1057, 0, 8}, // SCC12 Bus 0 - {1290, 0, 8}, // SCC13 Bus 0 - {905, 0, 8}, // SCC14 Bus 0 - {909, 0, 8}, // FCA11 Bus 0 - {1155, 0, 8}, // FCA12 Bus 0 + {0x340, 0, 8}, // LKAS11 Bus 0 + {0x4F1, 2, 4}, // CLU11 Bus 2 + {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 + {0x38D, 0, 8}, // FCA11 Bus 0 + {0x7D0, 0, 8}, // FCA12 Bus 0 }; AddrCheckStruct hyundai_addr_checks[] = { - {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {881, 0, 8, .expected_timestep = 10000U}, { 0 }}}, - {.msg = {{902, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{916, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{1057, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {0x371, 0, 8, .expected_timestep = 10000U}, { 0 }}}, + {.msg = {{0x386, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x394, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x421, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_ADDR_CHECK_LEN (sizeof(hyundai_addr_checks) / sizeof(hyundai_addr_checks[0])) AddrCheckStruct hyundai_cam_scc_addr_checks[] = { - {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {881, 0, 8, .expected_timestep = 10000U}, { 0 }}}, - {.msg = {{902, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{916, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{1057, 2, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {0x371, 0, 8, .expected_timestep = 10000U}, { 0 }}}, + {.msg = {{0x386, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x394, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x421, 2, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_CAM_SCC_ADDR_CHECK_LEN (sizeof(hyundai_cam_scc_addr_checks) / sizeof(hyundai_cam_scc_addr_checks[0])) AddrCheckStruct hyundai_long_addr_checks[] = { - {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {881, 0, 8, .expected_timestep = 10000U}, { 0 }}}, - {.msg = {{902, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{916, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{1265, 0, 4, .check_checksum = false, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {0x371, 0, 8, .expected_timestep = 10000U}, { 0 }}}, + {.msg = {{0x386, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x394, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x4F1, 0, 4, .check_checksum = false, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_LONG_ADDR_CHECK_LEN (sizeof(hyundai_long_addr_checks) / sizeof(hyundai_long_addr_checks[0])) // older hyundai models have less checks due to missing counters and checksums AddrCheckStruct hyundai_legacy_addr_checks[] = { - {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {881, 0, 8, .expected_timestep = 10000U}, { 0 }}}, - {.msg = {{902, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{916, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{1057, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {0x371, 0, 8, .expected_timestep = 10000U}, { 0 }}}, + {.msg = {{0x386, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x394, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x421, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_LEGACY_ADDR_CHECK_LEN (sizeof(hyundai_legacy_addr_checks) / sizeof(hyundai_legacy_addr_checks[0])) AddrCheckStruct hyundai_non_scc_addr_checks[] = { - {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, - {881, 0, 8, .expected_timestep = 10000U}, { 0 }}}, - {.msg = {{871, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, - {.msg = {{902, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, + {0x371, 0, 8, .expected_timestep = 10000U}, { 0 }}}, + {.msg = {{0x367, 0, 8, .expected_timestep = 10000U}, { 0 }, { 0 }}}, + {.msg = {{0x386, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, }; #define HYUNDAI_NON_SCC_ADDR_CHECK_LEN (sizeof(hyundai_non_scc_addr_checks) / sizeof(hyundai_non_scc_addr_checks[0])) -const int HYUNDAI_PARAM_LFA_BTN = 128; -const int HYUNDAI_PARAM_ESCC = 256; -const int HYUNDAI_PARAM_NON_SCC = 512; +const int HYUNDAI_PARAM_LFA_BTN = 256; +const int HYUNDAI_PARAM_ESCC = 512; +const int HYUNDAI_PARAM_NON_SCC = 1024; bool hyundai_legacy = false; bool hyundai_lfa_button = false; @@ -123,15 +123,15 @@ static uint8_t hyundai_get_counter(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t cnt; - if (addr == 608) { + if (addr == 0x260) { cnt = (GET_BYTE(to_push, 7) >> 4) & 0x3U; - } else if (addr == 902) { + } else if (addr == 0x386) { cnt = ((GET_BYTE(to_push, 3) >> 6) << 2) | (GET_BYTE(to_push, 1) >> 6); - } else if (addr == 916) { + } else if (addr == 0x394) { cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7U; - } else if (addr == 1057) { + } else if (addr == 0x421) { cnt = GET_BYTE(to_push, 7) & 0xFU; - } else if (addr == 1265) { + } else if (addr == 0x4F1) { cnt = (GET_BYTE(to_push, 3) >> 4) & 0xFU; } else { cnt = 0; @@ -143,13 +143,13 @@ static uint32_t hyundai_get_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum; - if (addr == 608) { + if (addr == 0x260) { chksum = GET_BYTE(to_push, 7) & 0xFU; - } else if (addr == 902) { + } else if (addr == 0x386) { chksum = ((GET_BYTE(to_push, 7) >> 6) << 2) | (GET_BYTE(to_push, 5) >> 6); - } else if (addr == 916) { + } else if (addr == 0x394) { chksum = GET_BYTE(to_push, 6) & 0xFU; - } else if (addr == 1057) { + } else if (addr == 0x421) { chksum = GET_BYTE(to_push, 7) >> 4; } else { chksum = 0; @@ -161,7 +161,7 @@ static uint32_t hyundai_compute_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum = 0; - if (addr == 902) { + if (addr == 0x386) { // count the bits for (int i = 0; i < 8; i++) { uint8_t b = GET_BYTE(to_push, i); @@ -178,12 +178,12 @@ static uint32_t hyundai_compute_checksum(CANPacket_t *to_push) { } else { // sum of nibbles for (int i = 0; i < 8; i++) { - if ((addr == 916) && (i == 7)) { + if ((addr == 0x394) && (i == 7)) { continue; // exclude } uint8_t b = GET_BYTE(to_push, i); - if (((addr == 608) && (i == 7)) || ((addr == 916) && (i == 6)) || ((addr == 1057) && (i == 7))) { - b &= (addr == 1057) ? 0x0FU : 0xF0U; // remove checksum + 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); } @@ -205,11 +205,11 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { // SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others if (valid && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) { // ACC main state - if (!hyundai_longitudinal && (addr == 1056)) { + if (!hyundai_longitudinal && (addr == 0x420)) { acc_main_on = GET_BIT(to_push, 0U); mads_acc_main_check(acc_main_on); } - if (addr == 1057) { + if (addr == 0x421) { // 2 bits: 13-14 int cruise_engaged = (GET_BYTES(to_push, 0, 4) >> 13) & 0x3U; hyundai_common_cruise_state_check(cruise_engaged); @@ -217,19 +217,19 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { } if (valid && (bus == 0)) { - if (addr == 593) { + if (addr == 0x251) { int torque_driver_new = ((GET_BYTES(to_push, 0, 4) & 0x7ffU) * 0.79) - 808; // scale down new driver torque signal to match previous one // update array of samples update_sample(&torque_driver, torque_driver_new); } - if ((addr == 913) && hyundai_lfa_button && mads_enabled) { + if ((addr == 0x391) && hyundai_lfa_button && mads_enabled) { bool lfa_pressed = GET_BIT(to_push, 4U); // LFA_PRESSED signal mads_lkas_button_check(lfa_pressed); } // ACC steering wheel buttons - if (addr == 1265) { + if (addr == 0x4F1) { int cruise_button = GET_BYTE(to_push, 0) & 0x7U; int main_button = GET_BIT(to_push, 3U); int main_button_prev = 0; @@ -243,12 +243,12 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { if (main_button && !main_button_prev) { main_enabled = !main_enabled; if (main_enabled && mads_enabled) { - controls_allowed = 1; + controls_allowed = true; } if (!main_enabled && main_enabled_prev) { disengageFromBrakes = false; - controls_allowed = 0; - controls_allowed_long = 0; + controls_allowed = false; + controls_allowed_long = false; } } main_button_prev = main_button; @@ -257,43 +257,43 @@ static int hyundai_rx_hook(CANPacket_t *to_push) { } if (hyundai_non_scc) { - if (addr == 871) { + if (addr == 0x367) { bool cruise_engaged = GET_BYTE(to_push, 0) != 0U; // CF_Lvr_CruiseSet signal hyundai_common_cruise_state_check(cruise_engaged); } - if (addr == 608) { + if (addr == 0x260) { acc_main_on = GET_BIT(to_push, 25U); // CRUISE_LAMP_M signal mads_acc_main_check(acc_main_on); } } // gas press, different for EV, hybrid, and ICE models - if ((addr == 881) && hyundai_ev_gas_signal) { + 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 == 881) && hyundai_hybrid_gas_signal) { + } else if ((addr == 0x371) && hyundai_hybrid_gas_signal) { gas_pressed = GET_BYTE(to_push, 7) != 0U; - } else if ((addr == 608) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { + } 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 == 902) { - uint32_t hyundai_speed = (GET_BYTES(to_push, 0, 4) & 0x3FFFU) + ((GET_BYTES(to_push, 4, 4) >> 16) & 0x3FFFU); // FL + RR - hyundai_speed /= 2; - vehicle_moving = hyundai_speed > HYUNDAI_STANDSTILL_THRSLD; + 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 == 916) { + if (addr == 0x394) { brake_pressed = GET_BIT(to_push, 55U) != 0U; } - bool stock_ecu_detected = (addr == 832); + bool stock_ecu_detected = (addr == 0x340); // If openpilot is controlling longitudinal we need to ensure the radar is turned off on non-camera SCC cars // Enforce by checking we don't see SCC12 - if (hyundai_longitudinal && (addr == 1057)) { + if (hyundai_longitudinal && (addr == 0x421)) { stock_ecu_detected = true; } generic_rx_checks(stock_ecu_detected); @@ -317,7 +317,7 @@ static int hyundai_tx_hook(CANPacket_t *to_send) { } // FCA11: Block any potential actuation - if ((addr == 909) && !hyundai_escc) { + if ((addr == 0x38D) && !hyundai_escc) { int CR_VSM_DecCmd = GET_BYTE(to_send, 1); int FCA_CmdAct = GET_BIT(to_send, 20U); int CF_VSM_DecCmdAct = GET_BIT(to_send, 31U); @@ -328,7 +328,7 @@ static int hyundai_tx_hook(CANPacket_t *to_send) { } // ACCEL: safety check - if (addr == 1057) { + 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; @@ -350,7 +350,7 @@ static int hyundai_tx_hook(CANPacket_t *to_send) { } // LKA STEER: safety check - if (addr == 832) { + if (addr == 0x340) { int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x7ffU) - 1024U; bool steer_req = GET_BIT(to_send, 27U) != 0U; @@ -361,14 +361,14 @@ static int hyundai_tx_hook(CANPacket_t *to_send) { } // UDS: Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address - if ((addr == 2000) && !hyundai_escc && !hyundai_camera_scc) { + if ((addr == 0x7D0) && !hyundai_escc && !hyundai_camera_scc) { if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { tx = 0; } } // BUTTONS: used for resume spamming and cruise cancellation - if ((addr == 1265) && !hyundai_longitudinal) { + if ((addr == 0x4F1) && !hyundai_longitudinal) { int button = GET_BYTE(to_send, 0) & 0x7U; bool allowed_resume = (button == 1) && controls_allowed && controls_allowed_long; @@ -391,10 +391,10 @@ static int hyundai_fwd_hook(int bus_num, int addr) { bus_fwd = 2; } if (bus_num == 2) { - int is_lkas11_msg = (addr == 832); - int is_lfahda_mfc_msg = (addr == 1157); - int is_scc_msg = (addr == 1056) || (addr == 1057) || (addr == 1290) || (addr == 905); - int is_fca_msg = (addr == 909) || (addr == 1155); + int is_lkas11_msg = (addr == 0x340); + int is_lfahda_mfc_msg = (addr == 0x485); + int is_scc_msg = (addr == 0x420) || (addr == 0x421) || (addr == 0x50A) || (addr == 0x389); + int is_fca_msg = (addr == 0x38D) || (addr == 0x483); int block_msg = is_lkas11_msg || is_lfahda_mfc_msg || ((is_scc_msg || is_fca_msg) && hyundai_longitudinal); if (!block_msg) { @@ -429,6 +429,7 @@ static const addr_checks* hyundai_legacy_init(uint16_t param) { hyundai_legacy = true; hyundai_longitudinal = false; hyundai_camera_scc = false; + hyundai_lfa_button = GET_FLAG(param, HYUNDAI_PARAM_LFA_BTN); hyundai_escc = GET_FLAG(param, HYUNDAI_PARAM_ESCC); hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC); diff --git a/board/safety/safety_hyundai_canfd.h b/board/safety/safety_hyundai_canfd.h index 24e441fb42..52f810903a 100644 --- a/board/safety/safety_hyundai_canfd.h +++ b/board/safety/safety_hyundai_canfd.h @@ -24,6 +24,12 @@ const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = { {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 @@ -116,9 +122,15 @@ uint16_t hyundai_canfd_crc_lut[256]; const int HYUNDAI_PARAM_CANFD_HDA2 = 16; const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; +const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128; bool hyundai_canfd_hda2 = false; 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(CANPacket_t *to_push) { uint8_t ret = 0; @@ -222,23 +234,23 @@ static int hyundai_canfd_rx_hook(CANPacket_t *to_push) { // vehicle moving if (addr == 0xa0) { - uint32_t speed = 0; - for (int i = 8; i < 15; i+=2) { - speed += GET_BYTE(to_push, i) | (GET_BYTE(to_push, i + 1) << 8U); - } - vehicle_moving = (speed / 4U) > HYUNDAI_STANDSTILL_THRSLD; + 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 (valid && (bus == scc_bus)) { // cruise state if ((addr == 0x1a0) && !hyundai_longitudinal) { - bool cruise_engaged = ((GET_BYTE(to_push, 8) >> 4) & 0x3U) != 0U; + // 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 ? 0x50 : 0x12a; + 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 @@ -257,7 +269,11 @@ static int hyundai_canfd_tx_hook(CANPacket_t *to_send) { int addr = GET_ADDR(to_send); if (hyundai_canfd_hda2 && !hyundai_longitudinal) { - tx = msg_allowed(to_send, HYUNDAI_CANFD_HDA2_TX_MSGS, sizeof(HYUNDAI_CANFD_HDA2_TX_MSGS)/sizeof(HYUNDAI_CANFD_HDA2_TX_MSGS[0])); + if (hyundai_canfd_hda2_alt_steering) { + tx = msg_allowed(to_send, HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS, sizeof(HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS)/sizeof(HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[0])); + } else { + tx = msg_allowed(to_send, HYUNDAI_CANFD_HDA2_TX_MSGS, sizeof(HYUNDAI_CANFD_HDA2_TX_MSGS)/sizeof(HYUNDAI_CANFD_HDA2_TX_MSGS[0])); + } } else if (hyundai_canfd_hda2 && hyundai_longitudinal) { tx = msg_allowed(to_send, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS, sizeof(HYUNDAI_CANFD_HDA2_LONG_TX_MSGS)/sizeof(HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[0])); } else { @@ -265,7 +281,7 @@ static int hyundai_canfd_tx_hook(CANPacket_t *to_send) { } // steering - const int steer_addr = (hyundai_canfd_hda2 && !hyundai_longitudinal) ? 0x50 : 0x12a; + 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) != 0U; @@ -328,16 +344,17 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { } if (bus_num == 2) { // LKAS for HDA2, LFA for HDA1 - int is_lkas_msg = (((addr == 0x50) || (addr == 0x2a4)) && hyundai_canfd_hda2); - int is_lfa_msg = ((addr == 0x12a) && !hyundai_canfd_hda2); + 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 - int is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); + bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); // CRUISE_INFO for non-HDA2, we send our own longitudinal commands - int is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_hda2); + bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_hda2); - int block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; + bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; if (!block_msg) { bus_fwd = 0; } @@ -352,6 +369,7 @@ static const addr_checks* hyundai_canfd_init(uint16_t param) { gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut); hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2); 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 ICE yet if (!hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { diff --git a/board/safety/safety_hyundai_common.h b/board/safety/safety_hyundai_common.h index 59a274bd76..663464548d 100644 --- a/board/safety/safety_hyundai_common.h +++ b/board/safety/safety_hyundai_common.h @@ -8,7 +8,7 @@ const int HYUNDAI_PARAM_CAMERA_SCC = 8; 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 = 30; // ~1kph +const uint32_t HYUNDAI_STANDSTILL_THRSLD = 12; // 0.375 kph enum { HYUNDAI_BTN_NONE = 0, @@ -48,12 +48,12 @@ void hyundai_common_cruise_state_check(const int cruise_engaged) { // 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 = 1; - controls_allowed_long = 1; + controls_allowed = true; + controls_allowed_long = true; } if (!cruise_engaged) { - controls_allowed_long = 0; + controls_allowed_long = false; } cruise_engaged_prev = cruise_engaged; } @@ -72,13 +72,13 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const int main 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 = 1; - controls_allowed_long = 1; + controls_allowed = true; + controls_allowed_long = true; } // exit controls on cancel press if (cruise_button == HYUNDAI_BTN_CANCEL) { - controls_allowed_long = 0; + controls_allowed_long = false; } cruise_button_prev = cruise_button; diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h index 8024efeb6e..899c3065c0 100644 --- a/board/safety/safety_subaru.h +++ b/board/safety/safety_subaru.h @@ -10,11 +10,21 @@ .type = TorqueDriverLimited, \ } \ -const SteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); +const SteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); const SteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); -const SteeringLimits SUBARU_STEERING_LIMITS_ALT = SUBARU_STEERING_LIMITS_GENERATOR(3071, 50, 70); +const SteeringLimits SUBARU_STEERING_LIMITS_ALT = SUBARU_STEERING_LIMITS_GENERATOR(3071, 50, 70); +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 @@ -22,6 +32,7 @@ const SteeringLimits SUBARU_STEERING_LIMITS_ALT = SUBARU_STEERING_LIMITS_GENERAT #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 @@ -29,16 +40,32 @@ const SteeringLimits SUBARU_STEERING_LIMITS_ALT = SUBARU_STEERING_LIMITS_GENERAT #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) \ - {MSG_SUBARU_ES_LKAS, 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_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_ADDR_CHECKS(alt_bus) \ {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}}, \ @@ -49,15 +76,28 @@ const SteeringLimits SUBARU_STEERING_LIMITS_ALT = SUBARU_STEERING_LIMITS_GENERAT {.msg = {{MSG_SUBARU_ES_LKAS_State, SUBARU_CAM_BUS, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 100000U}, { 0 }, { 0 }}},\ const CanMsg SUBARU_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS) + SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS) }; #define SUBARU_TX_MSGS_LEN (sizeof(SUBARU_TX_MSGS) / sizeof(SUBARU_TX_MSGS[0])) +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) +}; +#define SUBARU_LONG_TX_MSGS_LEN (sizeof(SUBARU_LONG_TX_MSGS) / sizeof(SUBARU_LONG_TX_MSGS[0])) + const CanMsg SUBARU_GEN2_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS) + SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS) }; #define SUBARU_GEN2_TX_MSGS_LEN (sizeof(SUBARU_GEN2_TX_MSGS) / sizeof(SUBARU_GEN2_TX_MSGS[0])) +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() +}; +#define SUBARU_GEN2_LONG_TX_MSGS_LEN (sizeof(SUBARU_GEN2_LONG_TX_MSGS) / sizeof(SUBARU_GEN2_LONG_TX_MSGS[0])) + AddrCheckStruct subaru_addr_checks[] = { SUBARU_COMMON_ADDR_CHECKS(SUBARU_MAIN_BUS) }; @@ -72,11 +112,14 @@ addr_checks subaru_gen2_rx_checks = {subaru_gen2_addr_checks, SUBARU_GEN2_ADDR_C const uint16_t SUBARU_PARAM_GEN2 = 1; -bool subaru_gen2 = false; +const uint16_t SUBARU_PARAM_LONGITUDINAL = 2; +const uint16_t SUBARU_PARAM_MAX_STEER_2018 = 4; -const uint16_t SUBARU_PARAM_MAX_STEER_2018 = 2; +bool subaru_gen2 = false; +bool subaru_longitudinal = false; bool subaru_max_steer_2018_crosstrek = false; + static uint32_t subaru_get_checksum(CANPacket_t *to_push) { return (uint8_t)GET_BYTE(to_push, 0); } @@ -102,7 +145,7 @@ static int subaru_rx_hook(CANPacket_t *to_push) { if (valid) { const int bus = GET_BUS(to_push); - const int alt_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; + 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)) { @@ -110,6 +153,11 @@ static int subaru_rx_hook(CANPacket_t *to_push) { 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); } if ((addr == MSG_SUBARU_ES_LKAS_State) && (bus == SUBARU_CAM_BUS) && mads_enabled) { @@ -118,7 +166,7 @@ static int subaru_rx_hook(CANPacket_t *to_push) { } // enter controls on rising edge of ACC, exit controls on ACC off - if ((addr == MSG_SUBARU_CruiseControl) && (bus == alt_bus)) { + if ((addr == MSG_SUBARU_CruiseControl) && (bus == alt_main_bus)) { bool cruise_engaged = GET_BIT(to_push, 41U) != 0U; pcm_cruise_check(cruise_engaged); @@ -127,12 +175,20 @@ static int subaru_rx_hook(CANPacket_t *to_push) { } // update vehicle moving with any non-zero wheel speed - if ((addr == MSG_SUBARU_Wheel_Speeds) && (bus == alt_bus)) { - vehicle_moving = ((GET_BYTES(to_push, 0, 4) >> 12) != 0U) || (GET_BYTES(to_push, 4, 4) != 0U); + 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); + + float speed = (fr + rr + rl + fl) / 4U * 0.057; + update_sample(&vehicle_speed, ROUND(speed * VEHICLE_SPEED_FACTOR)); } - if ((addr == MSG_SUBARU_Brake_Status) && (bus == alt_bus)) { - brake_pressed = ((GET_BYTE(to_push, 7) >> 6) & 1U); + if ((addr == MSG_SUBARU_Brake_Status) && (bus == alt_main_bus)) { + brake_pressed = GET_BIT(to_push, 62U) != 0U; } if ((addr == MSG_SUBARU_Throttle) && (bus == SUBARU_MAIN_BUS)) { @@ -148,11 +204,16 @@ static int subaru_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); - - if (subaru_gen2) { - tx = msg_allowed(to_send, SUBARU_GEN2_TX_MSGS, SUBARU_GEN2_TX_MSGS_LEN); + bool violation = false; + + if (subaru_gen2 && subaru_longitudinal) { + tx = msg_allowed(to_send, SUBARU_GEN2_LONG_TX_MSGS, SUBARU_GEN2_LONG_TX_MSGS_LEN); + } else if (subaru_gen2) { + tx = msg_allowed(to_send, SUBARU_GEN2_TX_MSGS, SUBARU_GEN2_TX_MSGS_LEN); + } else if (subaru_longitudinal) { + tx = msg_allowed(to_send, SUBARU_LONG_TX_MSGS, SUBARU_LONG_TX_MSGS_LEN); } else { - tx = msg_allowed(to_send, SUBARU_TX_MSGS, SUBARU_TX_MSGS_LEN); + tx = msg_allowed(to_send, SUBARU_TX_MSGS, SUBARU_TX_MSGS_LEN); } // steer cmd checks @@ -160,12 +221,52 @@ static int subaru_tx_hook(CANPacket_t *to_send) { 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) != 0U; + const SteeringLimits limits = subaru_gen2 ? SUBARU_GEN2_STEERING_LIMITS : subaru_max_steer_2018_crosstrek ? SUBARU_STEERING_LIMITS_ALT : SUBARU_STEERING_LIMITS; - if (steer_torque_cmd_checks(desired_torque, -1, limits)) { - tx = 0; + 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) & 0xFFFU); + bool cruise_cancel = GET_BIT(to_send, 56U) != 0U; + + 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) & 0xFFFU); + 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 = 0; } return tx; } @@ -174,7 +275,7 @@ 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; // forward to camera + bus_fwd = SUBARU_CAM_BUS; // to the eyesight camera } if (bus_num == SUBARU_CAM_BUS) { @@ -183,7 +284,13 @@ static int subaru_fwd_hook(int bus_num, int addr) { (addr == MSG_SUBARU_ES_DashStatus) || (addr == MSG_SUBARU_ES_LKAS_State) || (addr == MSG_SUBARU_ES_Infotainment)); - if (!block_lkas) { + + 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 } } @@ -195,6 +302,10 @@ static const addr_checks* subaru_init(uint16_t param) { subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2); subaru_max_steer_2018_crosstrek = GET_FLAG(param, SUBARU_PARAM_MAX_STEER_2018); +#ifdef ALLOW_DEBUG + subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL); +#endif + if (subaru_gen2) { subaru_rx_checks = (addr_checks){subaru_gen2_addr_checks, SUBARU_GEN2_ADDR_CHECK_LEN}; } else { diff --git a/board/safety/safety_subaru_preglobal.h b/board/safety/safety_subaru_preglobal.h index d96199cf4d..d9ea639148 100644 --- a/board/safety/safety_subaru_preglobal.h +++ b/board/safety/safety_subaru_preglobal.h @@ -101,7 +101,9 @@ static int subaru_preglobal_tx_hook(CANPacket_t *to_send) { int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 8) & 0x1FFFU); desired_torque = -1 * to_signed(desired_torque, 13); - if (steer_torque_cmd_checks(desired_torque, -1, SUBARU_PG_STEERING_LIMITS)) { + bool steer_req = (GET_BIT(to_send, 24U) != 0U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, SUBARU_PG_STEERING_LIMITS)) { tx = 0; } diff --git a/board/safety/safety_volkswagen_mqb.h b/board/safety/safety_volkswagen_mqb.h index ac67420652..2a0b9bcbbc 100644 --- a/board/safety/safety_volkswagen_mqb.h +++ b/board/safety/safety_volkswagen_mqb.h @@ -222,7 +222,9 @@ static int volkswagen_mqb_tx_hook(CANPacket_t *to_send) { desired_torque *= -1; } - if (steer_torque_cmd_checks(desired_torque, -1, VOLKSWAGEN_MQB_STEERING_LIMITS)) { + bool steer_req = GET_BIT(to_send, 30U) != 0U; + + if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_MQB_STEERING_LIMITS)) { tx = 0; } } diff --git a/board/safety/safety_volkswagen_pq.h b/board/safety/safety_volkswagen_pq.h index e5d42cdd57..9b12018ab4 100644 --- a/board/safety/safety_volkswagen_pq.h +++ b/board/safety/safety_volkswagen_pq.h @@ -147,7 +147,7 @@ static int volkswagen_pq_rx_hook(CANPacket_t *to_push) { // 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) == 1U) { - controls_allowed_long = 0; + controls_allowed_long = false; } } } else { diff --git a/board/safety_declarations.h b/board/safety_declarations.h index f92f29ccfd..a1c418c120 100644 --- a/board/safety_declarations.h +++ b/board/safety_declarations.h @@ -90,6 +90,11 @@ typedef struct { 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; @@ -163,6 +168,7 @@ bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const 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(CANPacket_t *to_send); void pcm_cruise_check(bool cruise_engaged); diff --git a/board/stm32fx/llbxcan.h b/board/stm32fx/llbxcan.h index 64f9905146..fd344913f7 100644 --- a/board/stm32fx/llbxcan.h +++ b/board/stm32fx/llbxcan.h @@ -22,19 +22,19 @@ void print(const char *a); 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 *CAN_obj, uint32_t speed, bool loopback, bool silent) { +bool llcan_set_speed(CAN_TypeDef *CANx, uint32_t speed, bool loopback, bool silent) { bool ret = true; // initialization mode - register_set(&(CAN_obj->MCR), CAN_MCR_TTCM | CAN_MCR_INRQ, 0x180FFU); + register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_INRQ, 0x180FFU); uint32_t timeout_counter = 0U; - while((CAN_obj->MSR & CAN_MSR_INAK) != CAN_MSR_INAK){ + 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(CAN_obj)); print(" set_speed timed out (1)!\n"); + print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (1)!\n"); ret = false; break; } @@ -42,30 +42,30 @@ bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool s if(ret){ // set time quanta from defines - register_set(&(CAN_obj->BTR), ((CAN_BTR_TS1_0 * (CAN_SEQ1-1U)) | + 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(&(CAN_obj->BTR), CAN_BTR_SILM | CAN_BTR_LBKM); + register_set_bits(&(CANx->BTR), CAN_BTR_SILM | CAN_BTR_LBKM); } if (silent) { - register_set_bits(&(CAN_obj->BTR), CAN_BTR_SILM); + register_set_bits(&(CANx->BTR), CAN_BTR_SILM); } // reset - register_set(&(CAN_obj->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU); + register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU); timeout_counter = 0U; - while(((CAN_obj->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) { + 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(CAN_obj)); print(" set_speed timed out (2)!\n"); + print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (2)!\n"); ret = false; break; } @@ -75,17 +75,17 @@ bool llcan_set_speed(CAN_TypeDef *CAN_obj, uint32_t speed, bool loopback, bool s return ret; } -void llcan_irq_disable(CAN_TypeDef *CAN_obj) { - if (CAN_obj == CAN1) { +void llcan_irq_disable(CAN_TypeDef *CANx) { + if (CANx == CAN1) { NVIC_DisableIRQ(CAN1_TX_IRQn); NVIC_DisableIRQ(CAN1_RX0_IRQn); NVIC_DisableIRQ(CAN1_SCE_IRQn); - } else if (CAN_obj == CAN2) { + } else if (CANx == CAN2) { NVIC_DisableIRQ(CAN2_TX_IRQn); NVIC_DisableIRQ(CAN2_RX0_IRQn); NVIC_DisableIRQ(CAN2_SCE_IRQn); #ifdef CAN3 - } else if (CAN_obj == CAN3) { + } else if (CANx == CAN3) { NVIC_DisableIRQ(CAN3_TX_IRQn); NVIC_DisableIRQ(CAN3_RX0_IRQn); NVIC_DisableIRQ(CAN3_SCE_IRQn); @@ -94,17 +94,17 @@ void llcan_irq_disable(CAN_TypeDef *CAN_obj) { } } -void llcan_irq_enable(CAN_TypeDef *CAN_obj) { - if (CAN_obj == CAN1) { +void llcan_irq_enable(CAN_TypeDef *CANx) { + if (CANx == CAN1) { NVIC_EnableIRQ(CAN1_TX_IRQn); NVIC_EnableIRQ(CAN1_RX0_IRQn); NVIC_EnableIRQ(CAN1_SCE_IRQn); - } else if (CAN_obj == CAN2) { + } else if (CANx == CAN2) { NVIC_EnableIRQ(CAN2_TX_IRQn); NVIC_EnableIRQ(CAN2_RX0_IRQn); NVIC_EnableIRQ(CAN2_SCE_IRQn); #ifdef CAN3 - } else if (CAN_obj == CAN3) { + } else if (CANx == CAN3) { NVIC_EnableIRQ(CAN3_TX_IRQn); NVIC_EnableIRQ(CAN3_RX0_IRQn); NVIC_EnableIRQ(CAN3_SCE_IRQn); @@ -113,21 +113,21 @@ void llcan_irq_enable(CAN_TypeDef *CAN_obj) { } } -bool llcan_init(CAN_TypeDef *CAN_obj) { +bool llcan_init(CAN_TypeDef *CANx) { bool ret = true; // Enter init mode - register_set_bits(&(CAN_obj->FMR), CAN_FMR_FINIT); + register_set_bits(&(CANx->FMR), CAN_FMR_FINIT); // Wait for INAK bit to be set uint32_t timeout_counter = 0U; - while(((CAN_obj->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) { + 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(CAN_obj)); print(" initialization timed out!\n"); + print(CAN_NAME_FROM_CANIF(CANx)); print(" initialization timed out!\n"); ret = false; break; } @@ -136,27 +136,27 @@ bool llcan_init(CAN_TypeDef *CAN_obj) { 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. - CAN_obj->sFilterRegister[0].FR1 = 0U; - CAN_obj->sFilterRegister[0].FR2 = 0U; - CAN_obj->sFilterRegister[14].FR1 = 0U; - CAN_obj->sFilterRegister[14].FR2 = 0U; - CAN_obj->FA1R |= 1U | (1U << 14); + CANx->sFilterRegister[0].FR1 = 0U; + CANx->sFilterRegister[0].FR2 = 0U; + CANx->sFilterRegister[14].FR1 = 0U; + CANx->sFilterRegister[14].FR2 = 0U; + CANx->FA1R |= 1U | (1U << 14); // Exit init mode, do not wait - register_clear_bits(&(CAN_obj->FMR), CAN_FMR_FINIT); + register_clear_bits(&(CANx->FMR), CAN_FMR_FINIT); // enable certain CAN interrupts - register_set_bits(&(CAN_obj->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); + 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 - CAN_obj->RF0R &= ~(CAN_RF0R_FOVR0); + CANx->RF0R &= ~(CAN_RF0R_FOVR0); - llcan_irq_enable(CAN_obj); + llcan_irq_enable(CANx); } return ret; } -void llcan_clear_send(CAN_TypeDef *CAN_obj) { - CAN_obj->TSR |= CAN_TSR_ABRQ0; // Abort message transmission on error interrupt - CAN_obj->MSR |= CAN_MSR_ERRI; // Clear error interrupt +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/board/stm32fx/lluart.h b/board/stm32fx/lluart.h index 4cdd337210..91c24dafca 100644 --- a/board/stm32fx/lluart.h +++ b/board/stm32fx/lluart.h @@ -21,31 +21,28 @@ void uart_tx_ring(uart_ring *q){ } void uart_rx_ring(uart_ring *q){ - // Do not read out directly if DMA enabled - if (q->dma_rx == false) { - ENTER_CRITICAL(); + ENTER_CRITICAL(); - // Read out RX buffer - uint8_t c = q->uart->DR; // This read after reading SR clears a bunch of interrupts + // 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; + 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; - } + 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); - } + // 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(); } + + EXIT_CRITICAL(); } void uart_send_break(uart_ring *u) { @@ -53,34 +50,6 @@ void uart_send_break(uart_ring *u) { u->uart->CR1 |= USART_CR1_SBK; } -// This function should be called on: -// * Half-transfer DMA interrupt -// * Full-transfer DMA interrupt -// * UART IDLE detection -uint32_t prev_w_index = 0; -void dma_pointer_handler(uart_ring *q, uint32_t dma_ndtr) { - ENTER_CRITICAL(); - uint32_t w_index = (q->rx_fifo_size - dma_ndtr); - - // Check for new data - if (w_index != prev_w_index){ - // Check for overflow - if ( - ((prev_w_index < q->r_ptr_rx) && (q->r_ptr_rx <= w_index)) || // No rollover - ((w_index < prev_w_index) && ((q->r_ptr_rx <= w_index) || (prev_w_index < q->r_ptr_rx))) // Rollover - ){ - // We lost data. Set the new read pointer to the oldest byte still available - q->r_ptr_rx = (w_index + 1U) % q->rx_fifo_size; - } - - // Set write pointer - q->w_ptr_rx = w_index; - } - - prev_w_index = w_index; - EXIT_CRITICAL(); -} - // 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); @@ -106,103 +75,28 @@ void uart_interrupt_handler(uart_ring *q) { // Send if necessary uart_tx_ring(q); - // Run DMA pointer handler if the line is idle - if(q->dma_rx && (status & USART_SR_IDLE)){ - // Reset IDLE flag - UART_READ_DR(q->uart) - - if(q == &uart_ring_gps){ - dma_pointer_handler(&uart_ring_gps, DMA2_Stream5->NDTR); - } else { - #ifdef DEBUG_UART - print("No IDLE dma_pointer_handler implemented for this UART."); - #endif - } - } - EXIT_CRITICAL(); } -void USART1_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_gps); } void USART2_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_debug); } void USART3_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin2); } void UART5_IRQ_Handler(void) { uart_interrupt_handler(&uart_ring_lin1); } -void DMA2_Stream5_IRQ_Handler(void) { - ENTER_CRITICAL(); - - // Handle errors - if((DMA2->HISR & DMA_HISR_TEIF5) || (DMA2->HISR & DMA_HISR_DMEIF5) || (DMA2->HISR & DMA_HISR_FEIF5)){ - #ifdef DEBUG_UART - print("Encountered UART DMA error. Clearing and restarting DMA...\n"); - #endif - - // Clear flags - DMA2->HIFCR = DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CFEIF5; - - // Re-enable the DMA if necessary - DMA2_Stream5->CR |= DMA_SxCR_EN; - } - - // Re-calculate write pointer and reset flags - dma_pointer_handler(&uart_ring_gps, DMA2_Stream5->NDTR); - DMA2->HIFCR = DMA_HIFCR_CTCIF5 | DMA_HIFCR_CHTIF5; - - EXIT_CRITICAL(); -} - // ***************************** Hardware setup ***************************** -void dma_rx_init(uart_ring *q) { - // Initialization is UART-dependent - if(q == &uart_ring_gps){ - // DMA2, stream 5, channel 4 - - // Disable FIFO mode (enable direct) - DMA2_Stream5->FCR &= ~DMA_SxFCR_DMDIS; - - // Setup addresses - DMA2_Stream5->PAR = (uint32_t)&(USART1->DR); // Source - DMA2_Stream5->M0AR = (uint32_t)q->elems_rx; // Destination - DMA2_Stream5->NDTR = q->rx_fifo_size; // Number of bytes to copy - - // Circular, Increment memory, byte size, periph -> memory, enable - // Transfer complete, half transfer, transfer error and direct mode error interrupt enable - DMA2_Stream5->CR = DMA_SxCR_CHSEL_2 | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_HTIE | DMA_SxCR_TCIE | DMA_SxCR_TEIE | DMA_SxCR_DMEIE | DMA_SxCR_EN; - - // Enable DMA receiver in UART - q->uart->CR3 |= USART_CR3_DMAR; - - // Enable UART IDLE interrupt - q->uart->CR1 |= USART_CR1_IDLEIE; - - // Enable interrupt - NVIC_EnableIRQ(DMA2_Stream5_IRQn); - } else { - print("Tried to initialize RX DMA for an unsupported UART\n"); - } -} - #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) { - if (u == USART1) { - // USART1 is on APB2 - u->BRR = __USART_BRR(48000000U, baud); - } else { - u->BRR = __USART_BRR(24000000U, baud); - } + u->BRR = __USART_BRR(APB1_FREQ*1000000U, baud); } void uart_init(uart_ring *q, int baud) { if(q->uart != NULL){ // Register interrupts (max data rate: 115200 baud) - if(q->uart == USART1){ - REGISTER_INTERRUPT(USART1_IRQn, USART1_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_1) - } else if (q->uart == USART2){ + if (q->uart == USART2){ REGISTER_INTERRUPT(USART2_IRQn, USART2_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_2) } else if (q->uart == USART3){ REGISTER_INTERRUPT(USART3_IRQn, USART3_IRQ_Handler, 150000U, FAULT_INTERRUPT_RATE_UART_3) @@ -211,9 +105,6 @@ void uart_init(uart_ring *q, int baud) { } else { // UART not used. Skip registering interrupts } - if(q->dma_rx){ - REGISTER_INTERRUPT(DMA2_Stream5_IRQn, DMA2_Stream5_IRQ_Handler, 100U, FAULT_INTERRUPT_RATE_UART_DMA) // Called twice per buffer - } // Set baud and enable peripheral with TX and RX mode uart_set_baud(q->uart, baud); @@ -223,9 +114,7 @@ void uart_init(uart_ring *q, int baud) { } // Enable UART interrupts - if(q->uart == USART1){ - NVIC_EnableIRQ(USART1_IRQn); - } else if (q->uart == USART2){ + if (q->uart == USART2){ NVIC_EnableIRQ(USART2_IRQn); } else if (q->uart == USART3){ NVIC_EnableIRQ(USART3_IRQn); @@ -234,10 +123,5 @@ void uart_init(uart_ring *q, int baud) { } else { // UART not used. Skip enabling interrupts } - - // Initialise RX DMA if used - if(q->dma_rx){ - dma_rx_init(q); - } } } diff --git a/board/stm32fx/peripherals.h b/board/stm32fx/peripherals.h index 3eeebf0eb6..5bdb8a056c 100644 --- a/board/stm32fx/peripherals.h +++ b/board/stm32fx/peripherals.h @@ -36,10 +36,6 @@ void common_init_gpio(void) { gpio_usb_init(); - // A9,A10: USART 1 for talking to the GPS - set_gpio_alternate(GPIOA, 9, GPIO_AF7_USART1); - set_gpio_alternate(GPIOA, 10, GPIO_AF7_USART1); - // B8,B9: CAN 1 #ifdef STM32F4 set_gpio_alternate(GPIOB, 8, GPIO_AF8_CAN1); @@ -59,16 +55,23 @@ void flasher_peripherals_init(void) { // Peripheral initialization void peripherals_init(void) { - // enable GPIOB, UART2, CAN, USB clock + // 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; - #ifdef PANDA + #ifndef PEDAL RCC->APB1ENR |= RCC_APB1ENR_UART5EN; #endif RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; @@ -76,21 +79,20 @@ void peripherals_init(void) { #ifdef CAN3 RCC->APB1ENR |= RCC_APB1ENR_CAN3EN; #endif + + // 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->APB1ENR |= RCC_APB1ENR_TIM12EN; // gmlan_alt - RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config - RCC->APB2ENR |= RCC_APB2ENR_USART1EN; - RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer - RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; - RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; - RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; RCC->APB2ENR |= RCC_APB2ENR_TIM9EN; // slow loop + RCC->APB1ENR |= RCC_APB1ENR_TIM12EN; // gmlan_alt } void enable_interrupt_timer(void) { diff --git a/board/stm32fx/stm32fx_config.h b/board/stm32fx/stm32fx_config.h index 1573426da8..dd026e98e4 100644 --- a/board/stm32fx/stm32fx_config.h +++ b/board/stm32fx/stm32fx_config.h @@ -48,10 +48,10 @@ #include "comms_definitions.h" #ifndef BOOTSTUB - #ifdef PANDA - #include "main_declarations.h" - #else + #ifdef PEDAL #include "pedal/main_declarations.h" + #else + #include "main_declarations.h" #endif #else #include "bootstub_declarations.h" @@ -73,17 +73,17 @@ #include "drivers/watchdog.h" #include "stm32fx/llflash.h" -#if defined(PANDA) || defined(BOOTSTUB) +#if !defined(PEDAL) || defined(BOOTSTUB) #include "drivers/spi.h" #include "stm32fx/llspi.h" #endif -#if !defined(BOOTSTUB) && (defined(PANDA) || defined(PEDAL_USB)) +#if !defined(BOOTSTUB) && (!defined(PEDAL) || defined(PEDAL_USB)) #include "drivers/uart.h" #include "stm32fx/lluart.h" #endif -#if !defined(PEDAL_USB) && !defined(PEDAL) && !defined(BOOTSTUB) +#if defined(PANDA) && !defined(BOOTSTUB) #include "stm32fx/llexti.h" #endif @@ -91,7 +91,7 @@ #include "stm32fx/llbxcan.h" #endif -#if defined(PANDA) || defined(BOOTSTUB) || defined(PEDAL_USB) +#if !defined(PEDAL) || defined(PEDAL_USB) || defined(BOOTSTUB) #include "stm32fx/llusb.h" #endif diff --git a/board/stm32h7/clock.h b/board/stm32h7/clock.h index b1846da261..c5f93cd02e 100644 --- a/board/stm32h7/clock.h +++ b/board/stm32h7/clock.h @@ -56,9 +56,6 @@ void clock_init(void) { // Set SysClock source to PLL register_set(&(RCC->CFGR), RCC_CFGR_SW_PLL1, 0x7U); while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL1); - - // SYSCFG peripheral clock enable - register_set_bits(&(RCC->AHB4ENR), RCC_APB4ENR_SYSCFGEN); //////////////END OTHER CLOCKS//////////////////// // Configure clock source for USB (HSI48) @@ -71,8 +68,4 @@ void clock_init(void) { register_set_bits(&(RCC->CR), RCC_CR_CSSHSEON); //Enable Vdd33usb supply level detector register_set_bits(&(PWR->CR3), PWR_CR3_USB33DEN); - - // Enable CPU access to SRAM1 and SRAM2 (in domain D2) - register_set_bits(&(RCC->AHB2ENR), RCC_AHB2ENR_SRAM1EN); - register_set_bits(&(RCC->AHB2ENR), RCC_AHB2ENR_SRAM2EN); } diff --git a/board/stm32h7/lladc.h b/board/stm32h7/lladc.h index 29d5e19d69..0a52b78d37 100644 --- a/board/stm32h7/lladc.h +++ b/board/stm32h7/lladc.h @@ -18,12 +18,12 @@ void adc_init(void) { } uint16_t adc_get_raw(uint8_t channel) { - ADC1->SQR1 &= ~(ADC_SQR1_L); ADC1->SQR1 = ((uint32_t) channel << 6U); - ADC1->SMPR1 = (0x7U << (channel * 3U) ); + ADC1->SMPR1 = (0x2U << (channel * 3U)); ADC1->PCSEL_RES0 = (0x1U << 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)); diff --git a/board/stm32h7/llfdcan.h b/board/stm32h7/llfdcan.h index 0382e8ce2e..5f13dfaa9c 100644 --- a/board/stm32h7/llfdcan.h +++ b/board/stm32h7/llfdcan.h @@ -20,7 +20,7 @@ // 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 30UL +#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) @@ -28,7 +28,7 @@ #define FDCAN_RX_FIFO_0_OFFSET 0UL // TX FIFO -#define FDCAN_TX_FIFO_EL_CNT 17UL +#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) @@ -46,16 +46,16 @@ 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 *CANx) { +bool fdcan_request_init(FDCAN_GlobalTypeDef *FDCANx) { bool ret = true; // Exit from sleep mode - CANx->CCCR &= ~(FDCAN_CCCR_CSR); - while ((CANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA); + FDCANx->CCCR &= ~(FDCAN_CCCR_CSR); + while ((FDCANx->CCCR & FDCAN_CCCR_CSA) == FDCAN_CCCR_CSA); // Request init uint32_t timeout_counter = 0U; - CANx->CCCR |= FDCAN_CCCR_INIT; - while ((CANx->CCCR & FDCAN_CCCR_INIT) == 0) { + FDCANx->CCCR |= FDCAN_CCCR_INIT; + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) == 0) { // Delay for about 1ms delay(10000); timeout_counter++; @@ -68,12 +68,12 @@ bool fdcan_request_init(FDCAN_GlobalTypeDef *CANx) { return ret; } -bool fdcan_exit_init(FDCAN_GlobalTypeDef *CANx) { +bool fdcan_exit_init(FDCAN_GlobalTypeDef *FDCANx) { bool ret = true; - CANx->CCCR &= ~(FDCAN_CCCR_INIT); + FDCANx->CCCR &= ~(FDCAN_CCCR_INIT); uint32_t timeout_counter = 0U; - while ((CANx->CCCR & FDCAN_CCCR_INIT) != 0) { + while ((FDCANx->CCCR & FDCAN_CCCR_INIT) != 0) { // Delay for about 1ms delay(10000); timeout_counter++; @@ -86,24 +86,24 @@ bool fdcan_exit_init(FDCAN_GlobalTypeDef *CANx) { return ret; } -bool llcan_set_speed(FDCAN_GlobalTypeDef *CANx, uint32_t speed, uint32_t data_speed, bool non_iso, bool loopback, bool silent) { +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(CANx); + bool ret = fdcan_request_init(FDCANx); if (ret) { // Enable config change - CANx->CCCR |= FDCAN_CCCR_CCE; + FDCANx->CCCR |= FDCAN_CCCR_CCE; //Reset operation mode to Normal - CANx->CCCR &= ~(FDCAN_CCCR_TEST); - CANx->TEST &= ~(FDCAN_TEST_LBCK); - CANx->CCCR &= ~(FDCAN_CCCR_MON); - CANx->CCCR &= ~(FDCAN_CCCR_ASM); - CANx->CCCR &= ~(FDCAN_CCCR_NISO); + 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) - //CANx->CCCR |= FDCAN_CCCR_ASM; + //FDCANx->CCCR |= FDCAN_CCCR_ASM; uint8_t prescaler = BITRATE_PRESCALER; if (speed < 2500U) { @@ -118,7 +118,7 @@ bool llcan_set_speed(FDCAN_GlobalTypeDef *CANx, uint32_t speed, uint32_t data_sp uint8_t seg2 = CAN_SEG2(tq, sp); uint8_t sjw = MIN(127U, seg2); - CANx->NBTP = (((sjw & 0x7FU)-1U)<NBTP = (((sjw & 0x7FU)-1U)<DBTP = (((sjw & 0xFU)-1U)<DBTP = (((sjw & 0xFU)-1U)<CCCR |= FDCAN_CCCR_NISO; + FDCANx->CCCR |= FDCAN_CCCR_NISO; } // Silent loopback is known as internal loopback in the docs if (loopback) { - CANx->CCCR |= FDCAN_CCCR_TEST; - CANx->TEST |= FDCAN_TEST_LBCK; - CANx->CCCR |= FDCAN_CCCR_MON; + 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) { - CANx->CCCR |= FDCAN_CCCR_MON; + FDCANx->CCCR |= FDCAN_CCCR_MON; } - ret = fdcan_exit_init(CANx); + ret = fdcan_exit_init(FDCANx); if (!ret) { - print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out! (2)\n"); + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (2)\n"); } } else { - print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out! (1)\n"); + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" set_speed timed out! (1)\n"); } return ret; } -void llcan_irq_disable(FDCAN_GlobalTypeDef *CANx) { - if (CANx == FDCAN1) { +void llcan_irq_disable(FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { NVIC_DisableIRQ(FDCAN1_IT0_IRQn); NVIC_DisableIRQ(FDCAN1_IT1_IRQn); - } else if (CANx == FDCAN2) { + } else if (FDCANx == FDCAN2) { NVIC_DisableIRQ(FDCAN2_IT0_IRQn); NVIC_DisableIRQ(FDCAN2_IT1_IRQn); - } else if (CANx == FDCAN3) { + } else if (FDCANx == FDCAN3) { NVIC_DisableIRQ(FDCAN3_IT0_IRQn); NVIC_DisableIRQ(FDCAN3_IT1_IRQn); } else { } } -void llcan_irq_enable(FDCAN_GlobalTypeDef *CANx) { - if (CANx == FDCAN1) { +void llcan_irq_enable(FDCAN_GlobalTypeDef *FDCANx) { + if (FDCANx == FDCAN1) { NVIC_EnableIRQ(FDCAN1_IT0_IRQn); NVIC_EnableIRQ(FDCAN1_IT1_IRQn); - } else if (CANx == FDCAN2) { + } else if (FDCANx == FDCAN2) { NVIC_EnableIRQ(FDCAN2_IT0_IRQn); NVIC_EnableIRQ(FDCAN2_IT1_IRQn); - } else if (CANx == FDCAN3) { + } else if (FDCANx == FDCAN3) { NVIC_EnableIRQ(FDCAN3_IT0_IRQn); NVIC_EnableIRQ(FDCAN3_IT1_IRQn); } else { } } -bool llcan_init(FDCAN_GlobalTypeDef *CANx) { - uint32_t can_number = CAN_NUM_FROM_CANIF(CANx); - bool ret = fdcan_request_init(CANx); +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 - CANx->CCCR |= FDCAN_CCCR_CCE; + FDCANx->CCCR |= FDCAN_CCCR_CCE; // Enable automatic retransmission - CANx->CCCR &= ~(FDCAN_CCCR_DAR); + FDCANx->CCCR &= ~(FDCAN_CCCR_DAR); // Enable transmission pause feature - CANx->CCCR |= FDCAN_CCCR_TXP; + FDCANx->CCCR |= FDCAN_CCCR_TXP; // Disable protocol exception handling - CANx->CCCR |= FDCAN_CCCR_PXHD; + FDCANx->CCCR |= FDCAN_CCCR_PXHD; // FD with BRS - CANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); + FDCANx->CCCR |= (FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE); // Set TX mode to FIFO - CANx->TXBC &= ~(FDCAN_TXBC_TFQM); + FDCANx->TXBC &= ~(FDCAN_TXBC_TFQM); // Configure TX element data size - CANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes + FDCANx->TXESC |= 0x7U << FDCAN_TXESC_TBDS_Pos; // 64 bytes //Configure RX FIFO0 element data size - CANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos; + FDCANx->RXESC |= 0x7U << FDCAN_RXESC_F0DS_Pos; // Disable filtering, accept all valid frames received - CANx->XIDFC &= ~(FDCAN_XIDFC_LSE); // No extended filters - CANx->SIDFC &= ~(FDCAN_SIDFC_LSS); // No standard filters - CANx->GFC &= ~(FDCAN_GFC_RRFE); // Accept extended remote frames - CANx->GFC &= ~(FDCAN_GFC_RRFS); // Accept standard remote frames - CANx->GFC &= ~(FDCAN_GFC_ANFE); // Accept extended frames to FIFO 0 - CANx->GFC &= ~(FDCAN_GFC_ANFS); // Accept standard frames to FIFO 0 + 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 - CANx->RXF0C |= (FDCAN_RX_FIFO_0_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_RXF0C_F0SA_Pos; - CANx->RXF0C |= FDCAN_RX_FIFO_0_EL_CNT << FDCAN_RXF0C_F0S_Pos; + 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 - CANx->RXF0C |= FDCAN_RXF0C_F0OM; + FDCANx->RXF0C |= FDCAN_RXF0C_F0OM; // TX FIFO (mode set earlier) - CANx->TXBC |= (FDCAN_TX_FIFO_OFFSET + (can_number * FDCAN_OFFSET_W)) << FDCAN_TXBC_TBSA_Pos; - CANx->TXBC |= FDCAN_TX_FIFO_EL_CNT << FDCAN_TXBC_TFQS_Pos; + 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); @@ -236,34 +236,34 @@ bool llcan_init(FDCAN_GlobalTypeDef *CANx) { } // Enable both interrupts for each module - CANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1); + FDCANx->ILE = (FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1); - CANx->IE &= 0x0U; // Reset all interrupts + FDCANx->IE &= 0x0U; // Reset all interrupts // Messages for INT0 - CANx->IE |= FDCAN_IE_RF0NE; // Rx FIFO 0 new message - CANx->IE |= FDCAN_IE_PEDE | FDCAN_IE_PEAE | FDCAN_IE_BOE | FDCAN_IE_EPE | FDCAN_IE_RF0LE; + 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??) - CANx->ILS |= FDCAN_ILS_TFEL; - CANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty + FDCANx->ILS |= FDCAN_ILS_TFEL; + FDCANx->IE |= FDCAN_IE_TFEE; // Tx FIFO empty - ret = fdcan_exit_init(CANx); + ret = fdcan_exit_init(FDCANx); if(!ret) { - print(CAN_NAME_FROM_CANIF(CANx)); print(" llcan_init timed out (2)!\n"); + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (2)!\n"); } - llcan_irq_enable(CANx); + llcan_irq_enable(FDCANx); } else { - print(CAN_NAME_FROM_CANIF(CANx)); print(" llcan_init timed out (1)!\n"); + print(CAN_NAME_FROM_CANIF(FDCANx)); print(" llcan_init timed out (1)!\n"); } return ret; } -void llcan_clear_send(FDCAN_GlobalTypeDef *CANx) { +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 - CANx->IR |= 0x3FCFFFFFU; // clear all interrupts - bool ret = llcan_init(CANx); + FDCANx->IR |= 0x3FCFFFFFU; // clear all interrupts + bool ret = llcan_init(FDCANx); UNUSED(ret); } diff --git a/board/stm32h7/lluart.h b/board/stm32h7/lluart.h index 6d1a363e27..0ad7b6a867 100644 --- a/board/stm32h7/lluart.h +++ b/board/stm32h7/lluart.h @@ -1,38 +1,27 @@ - -void dma_pointer_handler(uart_ring *q, uint32_t dma_ndtr) { UNUSED(q); UNUSED(dma_ndtr); } -void dma_rx_init(uart_ring *q) { UNUSED(q); } - -#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_rx_ring(uart_ring *q){ // Do not read out directly if DMA enabled - if (q->dma_rx == false) { - ENTER_CRITICAL(); + ENTER_CRITICAL(); - // Read out RX buffer - uint8_t c = q->uart->RDR; // This read after reading SR clears a bunch of interrupts + // 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; + 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; - } + 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); - } + // 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(); } + + EXIT_CRITICAL(); } void uart_tx_ring(uart_ring *q){ @@ -96,16 +85,6 @@ void uart_interrupt_handler(uart_ring *q) { // Send if necessary uart_tx_ring(q); - // Run DMA pointer handler if the line is idle - if(q->dma_rx && (status & USART_ISR_IDLE)){ - // Reset IDLE flag - UART_READ_RDR(q->uart) - - #ifdef DEBUG_UART - print("No IDLE dma_pointer_handler implemented for this UART."); - #endif - } - EXIT_CRITICAL(); } @@ -115,10 +94,6 @@ 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) - if (q->dma_rx) { - // TODO - } - uart_set_baud(q->uart, baud); q->uart->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE; @@ -127,10 +102,5 @@ void uart_init(uart_ring *q, int baud) { // Enable UART interrupts NVIC_EnableIRQ(UART7_IRQn); - - // Initialise RX DMA if used - if (q->dma_rx) { - dma_rx_init(q); - } } } diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index 7e3b2e5b16..b60f19016d 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -40,19 +40,6 @@ void common_init_gpio(void) { set_gpio_mode(GPIOE, 4, MODE_OUTPUT); set_gpio_output_type(GPIOE, 4, OUTPUT_TYPE_OPEN_DRAIN); - // F7,F8,F9,F10: BOARD ID - set_gpio_pullup(GPIOF, 7, PULL_NONE); - set_gpio_mode(GPIOF, 7, MODE_INPUT); - - set_gpio_pullup(GPIOF, 8, PULL_NONE); - set_gpio_mode(GPIOF, 8, MODE_INPUT); - - set_gpio_pullup(GPIOF, 9, PULL_NONE); - set_gpio_mode(GPIOF, 9, MODE_INPUT); - - set_gpio_pullup(GPIOF, 10, PULL_NONE); - set_gpio_mode(GPIOF, 10, MODE_INPUT); - //C4,A1: OBD_SBU1, OBD_SBU2 set_gpio_pullup(GPIOC, 4, PULL_NONE); set_gpio_mode(GPIOC, 4, MODE_ANALOG); @@ -110,29 +97,40 @@ void peripherals_init(void) { RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; - RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI + // 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->APB1LENR |= RCC_APB1LENR_UART7EN; // SOM uart - RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // tick timer RCC->APB1LENR |= RCC_APB1LENR_TIM12EN; // slow loop - RCC->APB1LENR |= RCC_APB1LENR_I2C5EN; // codec I2C - RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer - - RCC->APB1HENR |= RCC_APB1HENR_FDCANEN; // FDCAN core enable - RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC clocks - RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; - - // HS USB enable, also LP is needed for CSleep state(__WFI()) - RCC->AHB1ENR |= RCC_AHB1ENR_USB1OTGHSEN; - RCC->AHB1LPENR |= RCC_AHB1LPENR_USB1OTGHSLPEN; - RCC->AHB1LPENR &= ~(RCC_AHB1LPENR_USB1OTGHSULPILPEN); +#ifdef PANDA_JUNGLE + RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC + RCC->AHB4ENR |= RCC_AHB4ENR_ADC3EN; // Enable ADC3 clocks +#endif } void enable_interrupt_timer(void) { diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index 4e6f99bfa6..f00b5794de 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -75,7 +75,7 @@ separate IRQs for RX and TX. #include "drivers/watchdog.h" #include "stm32h7/llflash.h" -#if !defined(BOOTSTUB) && defined(PANDA) +#if !defined(BOOTSTUB) #include "drivers/uart.h" #include "stm32h7/lluart.h" #endif @@ -98,7 +98,7 @@ separate IRQs for RX and TX. 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 = 0; GPIOB->MODER = 0; GPIOC->MODER = 0; GPIOD->MODER = 0; GPIOE->MODER = 0; GPIOF->MODER = 0; GPIOG->MODER = 0; GPIOH->MODER = 0; + GPIOA->MODER = 0xAB000000; 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/board/stm32h7/stm32h7x5_flash.ld b/board/stm32h7/stm32h7x5_flash.ld index 5aef663743..3b6eee5dd0 100644 --- a/board/stm32h7/stm32h7x5_flash.ld +++ b/board/stm32h7/stm32h7x5_flash.ld @@ -64,12 +64,17 @@ _Min_Stack_Size = 0x400; /* required amount of stack */ /* Specify the memory areas */ MEMORY { +/* RAM */ +BACKUP_SRAM (xrw) : ORIGIN = 0x38800000, LENGTH = 4K /* Backup SRAM(4kb) */ +SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K /* SRAM4(16kb) best for BDMA and SDMMC1*/ +SRAM12 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K /* SRAM1(16kb) + SRAM2(16kb), not for BDMA or SDMMC1 */ +AXISRAM (xrw) : ORIGIN = 0x24000000, LENGTH = 320K /* AXI SRAM */ DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K /* DTCM */ -RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K /* AXI SRAM */ -RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K /* SRAM1(16kb) + SRAM2(16kb) */ -RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K /* SRAM4 */ + +/* Code */ +SYSTEM (rx) : ORIGIN = 0x1FF00000, LENGTH = 128K /* System memory */ +FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K -FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K } /* Define output sections */ @@ -141,7 +146,7 @@ SECTIONS _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : + .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ @@ -180,17 +185,35 @@ SECTIONS . = ALIGN(8); } >DTCMRAM - .ram_d1 (NOLOAD) : + .itcmram (NOLOAD) : + { + . = ALIGN(4); + *(.itcmram*) + } >ITCMRAM + + .axisram (NOLOAD) : + { + . = ALIGN(4); + *(.axisram*) + } >AXISRAM + + .sram12 (NOLOAD) : + { + . = ALIGN(4); + *(.sram12*) + } >SRAM12 + + .sram4 (NOLOAD) : { . = ALIGN(4); - *(.ram_d1*) - } >RAM_D1 + *(.sram4*) + } >SRAM4 - .ram_d2 (NOLOAD) : + .backup_sram (NOLOAD) : { . = ALIGN(4); - *(.ram_d2*) - } >RAM_D2 + *(.backup_sram*) + } >BACKUP_SRAM .ARM.attributes 0 : { *(.ARM.attributes) } } diff --git a/examples/tesla_tester.py b/examples/tesla_tester.py index c849b6378d..966e39d103 100644 --- a/examples/tesla_tester.py +++ b/examples/tesla_tester.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# flake8: noqa import binascii from panda import Panda @@ -16,7 +15,8 @@ def tesla_tester(): 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: + # 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) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..bac2d7f081 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +# https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml +[tool.ruff] +select = ["E", "F", "W", "PIE", "C4", "ISC", "RUF100", "A"] +ignore = ["W292", "E741", "E402", "C408", "ISC003"] +line-length = 160 +target-version="py311" +flake8-implicit-str-concat.allow-multiline=false diff --git a/python/__init__.py b/python/__init__.py index 0729050e48..9a22e45e44 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -7,9 +7,8 @@ import hashlib import binascii import datetime -import warnings import logging -from functools import wraps +from functools import wraps, partial from typing import Optional from itertools import accumulate @@ -91,35 +90,21 @@ def unpack_can_buffer(dat): return (ret, dat) -def ensure_health_packet_version(fn): - @wraps(fn) - def wrapper(self, *args, **kwargs): - if self.health_version < self.HEALTH_PACKET_VERSION: - raise RuntimeError("Panda firmware has outdated health packet definition. Reflash panda firmware.") - elif self.health_version > self.HEALTH_PACKET_VERSION: - raise RuntimeError("Panda python library has outdated health packet definition. Update panda python library.") - return fn(self, *args, **kwargs) - return wrapper -def ensure_can_packet_version(fn): +def ensure_version(desc, lib_field, panda_field, fn): @wraps(fn) def wrapper(self, *args, **kwargs): - if self.can_version < self.CAN_PACKET_VERSION: - raise RuntimeError("Panda firmware has outdated CAN packet definition. Reflash panda firmware.") - elif self.can_version > self.CAN_PACKET_VERSION: - raise RuntimeError("Panda python library has outdated CAN packet definition. Update panda python library.") + lib_version = getattr(self, lib_field) + panda_version = getattr(self, panda_field) + if lib_version != panda_version: + raise RuntimeError(f"{desc} packet version mismatch: panda's firmware v{panda_version}, library v{lib_version}. Reflash panda.") return fn(self, *args, **kwargs) return wrapper +ensure_can_packet_version = partial(ensure_version, "CAN", "CAN_PACKET_VERSION", "can_version") +ensure_can_health_packet_version = partial(ensure_version, "CAN health", "CAN_HEALTH_PACKET_VERSION", "can_health_version") +ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_VERSION", "health_version") + -def ensure_can_health_packet_version(fn): - @wraps(fn) - def wrapper(self, *args, **kwargs): - if self.can_health_version < self.CAN_HEALTH_PACKET_VERSION: - raise RuntimeError("Panda firmware has outdated CAN health packet definition. Reflash panda firmware.") - elif self.can_health_version > self.CAN_HEALTH_PACKET_VERSION: - raise RuntimeError("Panda python library has outdated CAN health packet definition. Update panda python library.") - return fn(self, *args, **kwargs) - return wrapper def parse_timestamp(dat): a = struct.unpack("HBBBBBB", dat) @@ -186,6 +171,7 @@ class Panda: GMLAN_CAN2 = 1 GMLAN_CAN3 = 2 + USB_PIDS = (0xddee, 0xddcc) REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE @@ -206,9 +192,9 @@ class Panda: HEALTH_STRUCT = struct.Struct(" bool: def reconnect(self): if self._handle_open: self.close() - time.sleep(1.0) success = False # wait up to 15 seconds - for i in range(0, 15): + for _ in range(0, 15*10): try: self.connect() success = True break except Exception: - logging.debug("reconnecting is taking %d seconds...", i + 1) - try: - dfu = PandaDFU(self.get_dfu_serial()) - dfu.recover() - except Exception: - pass - time.sleep(1.0) + pass + time.sleep(0.1) if not success: raise Exception("reconnect failed") @@ -784,13 +768,6 @@ def set_power_save(self, power_save_enabled=0): def enable_deepsleep(self): self._handle.controlWrite(Panda.REQUEST_OUT, 0xfb, 0, 0, b'') - def set_esp_power(self, on): - self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'') - - def esp_reset(self, bootmode=0): - self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'') - time.sleep(0.2) - def set_safety_mode(self, mode=SAFETY_SILENT, param=0): self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'') @@ -906,7 +883,7 @@ def serial_read(self, port_number): def serial_write(self, port_number, ln): ret = 0 - if type(ln) == str: + if isinstance(ln, str): ln = bytes(ln, 'utf-8') for i in range(0, len(ln), 0x20): ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i + 0x20]) @@ -1031,6 +1008,12 @@ def set_siren(self, enabled): def set_green_led(self, enabled): self._handle.controlWrite(Panda.REQUEST_OUT, 0xf7, int(enabled), 0, b'') + def set_clock_source_period(self, period): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, period, 0, b'') + + def force_relay_drive(self, intercept_relay_drive, ignition_relay_drive): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xc5, (int(intercept_relay_drive) | int(ignition_relay_drive) << 1), 0, b'') + # ****************** Logging ***************** def get_logs(self, last_id=None, get_all=False): assert (last_id is None) or (0 <= last_id < 0xFFFF) diff --git a/python/base.py b/python/base.py index d19a2b8612..509d3ebf77 100644 --- a/python/base.py +++ b/python/base.py @@ -15,7 +15,7 @@ def close(self) -> None: ... @abstractmethod - def controlWrite(self, request_type: int, request: int, value: int, index: int, data, timeout: int = TIMEOUT) -> int: + def controlWrite(self, request_type: int, request: int, value: int, index: int, data, timeout: int = TIMEOUT, expect_disconnect: bool = False): ... @abstractmethod diff --git a/tests/pedal/canhandle.py b/python/canhandle.py similarity index 91% rename from tests/pedal/canhandle.py rename to python/canhandle.py index 0d7582f048..ff6e625552 100644 --- a/tests/pedal/canhandle.py +++ b/python/canhandle.py @@ -1,8 +1,10 @@ import struct import signal +from .base import BaseHandle -class CanHandle(object): + +class CanHandle(BaseHandle): def __init__(self, p, bus): self.p = p self.bus = bus @@ -29,7 +31,10 @@ def _handle_timeout(signum, frame): return ret - def controlWrite(self, request_type, request, value, index, data, timeout=0): + def close(self): + pass + + def controlWrite(self, request_type, request, value, index, data, timeout=0, expect_disconnect=False): # ignore data in reply, panda doesn't use it return self.controlRead(request_type, request, value, index, 0, timeout) diff --git a/python/dfu.py b/python/dfu.py index bebc243ced..fc196e4a84 100644 --- a/python/dfu.py +++ b/python/dfu.py @@ -14,9 +14,9 @@ class PandaDFU: def __init__(self, dfu_serial: Optional[str]): # try USB, then SPI handle: Optional[BaseSTBootloaderHandle] - handle = PandaDFU.usb_connect(dfu_serial) + self._context, handle = PandaDFU.usb_connect(dfu_serial) if handle is None: - handle = PandaDFU.spi_connect(dfu_serial) + self._context, handle = PandaDFU.spi_connect(dfu_serial) if handle is None: raise Exception(f"failed to open DFU device {dfu_serial}") @@ -24,8 +24,21 @@ def __init__(self, dfu_serial: Optional[str]): self._handle: BaseSTBootloaderHandle = handle self._mcu_type: McuType = self._handle.get_mcu_type() + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + def close(self): + if self._handle is not None: + self._handle.close() + self._handle = None + if self._context is not None: + self._context.close() + @staticmethod - def usb_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderUSBHandle]: + def usb_connect(dfu_serial: Optional[str]): handle = None context = usb1.USBContext() context.open() @@ -40,10 +53,10 @@ def usb_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderUSBHandle]: handle = STBootloaderUSBHandle(device, device.open()) break - return handle + return context, handle @staticmethod - def spi_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderSPIHandle]: + def spi_connect(dfu_serial: Optional[str]): handle = None this_dfu_serial = None @@ -56,10 +69,10 @@ def spi_connect(dfu_serial: Optional[str]) -> Optional[STBootloaderSPIHandle]: if dfu_serial is not None and dfu_serial != this_dfu_serial: handle = None - return handle + return None, handle @staticmethod - def list() -> List[str]: + def list() -> List[str]: # noqa: A003 ret = PandaDFU.usb_list() ret += PandaDFU.spi_list() return list(set(ret)) @@ -82,7 +95,7 @@ def usb_list() -> List[str]: @staticmethod def spi_list() -> List[str]: try: - h = PandaDFU.spi_connect(None) + _, h = PandaDFU.spi_connect(None) if h is not None: dfu_serial = PandaDFU.st_serial_to_dfu_serial(h.get_uid(), h.get_mcu_type()) return [dfu_serial, ] diff --git a/python/isotp.py b/python/isotp.py index 45acb6abae..3334deb8ed 100644 --- a/python/isotp.py +++ b/python/isotp.py @@ -6,10 +6,8 @@ def msg(x): if DEBUG: print("S:", binascii.hexlify(x)) - if len(x) <= 7: - ret = bytes([len(x)]) + x - else: - assert False + assert len(x) <= 7 + ret = bytes([len(x)]) + x return ret.ljust(8, b"\x00") kmsgs = [] @@ -56,7 +54,7 @@ def isotp_recv_subaddr(panda, addr, bus, sendaddr, subaddr): dat = msg[2:] else: print(binascii.hexlify(msg)) - assert False + raise AssertionError return dat[0:tlen] @@ -133,7 +131,7 @@ def isotp_recv(panda, addr, bus=0, sendaddr=None, subaddr=None): tlen = msg[0] & 0xf dat = msg[1:] else: - assert False + raise AssertionError dat = dat[0:tlen] if DEBUG: diff --git a/python/serial.py b/python/serial.py index 97fe8f1d90..9ac58862b5 100644 --- a/python/serial.py +++ b/python/serial.py @@ -8,7 +8,7 @@ def __init__(self, panda, port, baud): self.panda.set_uart_baud(self.port, baud) self.buf = b"" - def read(self, l=1): # noqa: E741 + def read(self, l=1): tt = self.panda.serial_read(self.port) if len(tt) > 0: self.buf += tt diff --git a/python/spi.py b/python/spi.py index ad0225b1e0..0be28f49c1 100644 --- a/python/spi.py +++ b/python/spi.py @@ -314,7 +314,7 @@ def __init__(self): self._mcu_type = MCU_TYPE_BY_IDCODE[self.get_chip_id()] except PandaSpiException: - raise PandaSpiException("failed to connect to panda") # pylint: disable=W0707 + raise PandaSpiException("failed to connect to panda") from None def _get_ack(self, spi, timeout=1.0): data = 0x00 diff --git a/requirements.txt b/requirements.txt index 1cfecd2989..41c5226aa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,19 @@ +ruff libusb1==2.0.1 numpy hexdump>=3.3 pycryptodome==3.9.8 tqdm>=4.14.0 pytest +pytest-mock +pytest-xdist pytest-timeouts parameterized requests -flake8==3.7.9 cffi==1.14.3 crcmod pre-commit==2.13.0 -pylint==2.15.4 scons==4.4.0 flaky spidev -pytest-mock +termcolor diff --git a/tests/black_loopback_test.py b/tests/black_loopback_test.py deleted file mode 100755 index bcfcaefede..0000000000 --- a/tests/black_loopback_test.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 - -# Loopback test between two black pandas (+ harness and power) -# 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 sys -import time -import random -import argparse - -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 - -def get_test_string(): - return b"test" + os.urandom(10) - -def run_test(sleep_duration): - pandas = Panda.list() - print(pandas) - - # make sure two pandas are connected - if len(pandas) != 2: - print("Connect white/grey and black panda to run this test!") - assert False - - # connect - pandas[0] = Panda(pandas[0]) - pandas[1] = Panda(pandas[1]) - - # find out the hardware types - if not pandas[0].is_black() or not pandas[1].is_black(): - print("Connect two black pandas to run this test!") - assert False - - for panda in pandas: - # disable safety modes - panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) - - # test health packet - print("panda health", panda.health()) - - # setup test array (send bus, sender obd, reciever obd, expected busses) - test_array = [ - (0, False, False, [0]), - (1, False, False, [1]), - (2, False, False, [2]), - (0, False, True, [0, 1]), - (1, False, True, []), - (2, False, True, [2]), - (0, True, False, [0]), - (1, True, False, [0]), - (2, True, False, [2]), - (0, True, True, [0, 1]), - (1, True, True, [0, 1]), - (2, True, True, [2]) - ] - - # test both orientations - print("***************** TESTING (0 --> 1) *****************") - test_buses(pandas[0], pandas[1], test_array, sleep_duration) - print("***************** TESTING (1 --> 0) *****************") - test_buses(pandas[1], pandas[0], test_array, sleep_duration) - - -def test_buses(send_panda, recv_panda, test_array, sleep_duration): - for send_bus, send_obd, recv_obd, recv_buses in test_array: - send_panda.send_heartbeat() - recv_panda.send_heartbeat() - print("\nSend bus:", send_bus, " Send OBD:", send_obd, " Recv OBD:", recv_obd) - - # set OBD on pandas - send_panda.set_gmlan(True if send_obd else None) - recv_panda.set_gmlan(True if recv_obd else None) - - # clear and flush - send_panda.can_clear(send_bus) - for recv_bus in recv_buses: - recv_panda.can_clear(recv_bus) - send_panda.can_recv() - recv_panda.can_recv() - - # send the characters - at = random.randint(1, 2000) - st = get_test_string()[0:8] - send_panda.can_send(at, st, send_bus) - time.sleep(0.1) - - # check for receive - _ = send_panda.can_recv() # cans echo - cans_loop = recv_panda.can_recv() - - loop_buses = [] - for loop in cans_loop: - print(" Loop on bus", str(loop[3])) - loop_buses.append(loop[3]) - if len(cans_loop) == 0: - print(" No loop") - - # test loop buses - recv_buses.sort() - loop_buses.sort() - assert recv_buses == loop_buses - print(" TEST PASSED") - - 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 i in range(args.n): - run_test(sleep_duration=args.sleep) diff --git a/tests/black_white_loopback_test.py b/tests/black_white_loopback_test.py index f5c6170fe4..5b2312befb 100755 --- a/tests/black_white_loopback_test.py +++ b/tests/black_white_loopback_test.py @@ -6,13 +6,10 @@ import os -import sys import time import random import argparse - -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) @@ -30,8 +27,7 @@ def run_test(sleep_duration): # make sure two pandas are connected if len(pandas) != 2: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # connect pandas[0] = Panda(pandas[0]) @@ -48,8 +44,7 @@ def run_test(sleep_duration): black_panda = pandas[1] other_panda = pandas[0] else: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # disable safety modes black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -130,8 +125,7 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): loop_buses.append(loop[3]) if len(cans_loop) == 0: print(" No loop") - if not os.getenv("NOASSERT"): - assert False + assert not os.getenv("NOASSERT") # test loop buses recv_buses.sort() @@ -141,8 +135,7 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): zero_bus_errors += 1 else: nonzero_bus_errors += 1 - if not os.getenv("NOASSERT"): - assert False + assert not os.getenv("NOASSERT") else: print(" TEST PASSED") @@ -159,5 +152,5 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): while True: run_test(sleep_duration=args.sleep) else: - for i in range(args.n): + for _ in range(args.n): run_test(sleep_duration=args.sleep) diff --git a/tests/black_white_relay_endurance.py b/tests/black_white_relay_endurance.py index f44eafd207..db19e72539 100755 --- a/tests/black_white_relay_endurance.py +++ b/tests/black_white_relay_endurance.py @@ -6,13 +6,11 @@ import os -import sys import time import random import argparse -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) @@ -30,8 +28,7 @@ def run_test(sleep_duration): # make sure two pandas are connected if len(pandas) != 2: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # connect pandas[0] = Panda(pandas[0]) @@ -48,8 +45,7 @@ def run_test(sleep_duration): black_panda = pandas[1] other_panda = pandas[0] else: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # disable safety modes black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -137,8 +133,7 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): loop_buses.append(loop[3]) if len(cans_loop) == 0: print(" No loop") - if not os.getenv("NOASSERT"): - assert False + assert os.getenv("NOASSERT") # test loop buses recv_buses.sort() @@ -148,8 +143,7 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): zero_bus_errors += 1 else: nonzero_bus_errors += 1 - if not os.getenv("NOASSERT"): - assert False + assert os.getenv("NOASSERT") else: print(" TEST PASSED") @@ -166,5 +160,5 @@ def test_buses(black_panda, other_panda, direction, test_array, sleep_duration): while True: run_test(sleep_duration=args.sleep) else: - for i in range(args.n): + for _ in range(args.n): run_test(sleep_duration=args.sleep) diff --git a/tests/black_white_relay_test.py b/tests/black_white_relay_test.py index 4de02ec849..90b33be681 100755 --- a/tests/black_white_relay_test.py +++ b/tests/black_white_relay_test.py @@ -5,13 +5,11 @@ import os -import sys import time import random import argparse -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) @@ -29,8 +27,7 @@ def run_test(sleep_duration): # make sure two pandas are connected if len(pandas) != 2: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # connect pandas[0] = Panda(pandas[0]) @@ -50,8 +47,7 @@ def run_test(sleep_duration): black_panda = pandas[1] other_panda = pandas[0] else: - print("Connect white/grey and black panda to run this test!") - assert False + raise Exception("Connect white/grey and black panda to run this test!") # disable safety modes black_panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) @@ -69,8 +65,7 @@ def run_test(sleep_duration): if not test_buses(black_panda, other_panda, (0, False, [0])): open_errors += 1 - print("Open error") - assert False + raise Exception("Open error") # Switch off relay black_panda.set_safety_mode(Panda.SAFETY_SILENT) @@ -78,8 +73,7 @@ def run_test(sleep_duration): if not test_buses(black_panda, other_panda, (0, False, [0, 2])): closed_errors += 1 - print("Close error") - assert False + raise Exception("Close error") counter += 1 print("Number of cycles:", counter, "Open errors:", open_errors, "Closed errors:", closed_errors, "Content errors:", content_errors) @@ -137,5 +131,5 @@ def test_buses(black_panda, other_panda, test_obj): while True: run_test(sleep_duration=args.sleep) else: - for i in range(args.n): + for _ in range(args.n): run_test(sleep_duration=args.sleep) diff --git a/tests/bulk_write_test.py b/tests/bulk_write_test.py index 047e870ce8..bc5bad3317 100755 --- a/tests/bulk_write_test.py +++ b/tests/bulk_write_test.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# type: ignore # for jungle stuff import os import time import threading @@ -9,7 +8,7 @@ JUNGLE = "JUNGLE" in os.environ if JUNGLE: - from panda_jungle import PandaJungle # pylint: disable=import-error + from panda import PandaJungle # The TX buffers on pandas is 0x100 in length. NUM_MESSAGES_PER_BUS = 10000 @@ -30,7 +29,7 @@ def flood_tx(panda): if len(serials) != 2: raise Exception("Connect two pandas to perform this test!") sender = Panda(serials[0]) - receiver = Panda(serials[1]) + receiver = Panda(serials[1]) # type: ignore receiver.set_safety_mode(Panda.SAFETY_ALLOUTPUT) sender.set_safety_mode(Panda.SAFETY_ALLOUTPUT) diff --git a/tests/can_printer.py b/tests/can_printer.py index af0ed0e2ed..15ce89f688 100755 --- a/tests/can_printer.py +++ b/tests/can_printer.py @@ -1,13 +1,10 @@ #!/usr/bin/env python3 - import os -import sys import time from collections import defaultdict import binascii -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda # fake def sec_since_boot(): @@ -30,7 +27,7 @@ def can_printer(): 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())])): + 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() diff --git a/tests/canfd/test_canfd.py b/tests/canfd/test_canfd.py index 508e0e3992..873bc796ba 100755 --- a/tests/canfd/test_canfd.py +++ b/tests/canfd/test_canfd.py @@ -3,8 +3,9 @@ import time import random from collections import defaultdict -from panda import Panda, calculate_checksum -from panda_jungle import PandaJungle # pylint: disable=import-error +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") @@ -12,13 +13,11 @@ if os.getenv("H7_PANDAS_EXCLUDE"): H7_PANDAS_EXCLUDE = os.getenv("H7_PANDAS_EXCLUDE").strip().split(" ") # type: ignore -#TODO: REMOVE, temporary list of CAN FD lengths, one in panda python lib MUST be used -DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48] - 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) @@ -40,6 +39,7 @@ 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: @@ -47,6 +47,34 @@ def panda_init(serial, enable_canfd=False, enable_non_iso=False): 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) @@ -77,9 +105,6 @@ def canfd_test(p_send, p_recv): for bus in range(3): assert not len(sent_msgs[bus]), f"loop {n}: bus {bus} missing {len(sent_msgs[bus])} messages" - print("Got all messages intact") - - def setup_test(enable_non_iso=False): panda_serials = panda_reset() @@ -97,6 +122,7 @@ def setup_test(enable_non_iso=False): 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): @@ -116,5 +142,11 @@ def main(): 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/tests/check_fw_size.py b/tests/check_fw_size.py new file mode 100755 index 0000000000..53681c5a3e --- /dev/null +++ b/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/tests/ci_reset_hw.py b/tests/ci_reset_hw.py index 6a5427318f..2cdd3f1f4f 100755 --- a/tests/ci_reset_hw.py +++ b/tests/ci_reset_hw.py @@ -1,9 +1,16 @@ #!/usr/bin/env python3 import concurrent.futures -from panda import Panda, PandaDFU +from panda import Panda, PandaDFU, PandaJungle from panda.tests.libs.resetter import Resetter +# all jungles used in the HITL tests +JUNGLES = [ + "058010800f51363038363036", + "23002d000851393038373731" +] + + # Reset + flash all CI hardware to get it into a consistent state # * ports 1-2 are jungles # * port 3 is for the USB hubs @@ -27,7 +34,7 @@ def recover(serial): pandas = Panda.list() print(pandas) - assert len(pandas) == 7 + assert len(pandas) >= 7 with concurrent.futures.ProcessPoolExecutor(max_workers=len(pandas)) as exc: def flash(serial): @@ -36,5 +43,14 @@ def flash(serial): pf.flash() list(exc.map(flash, pandas, timeout=20)) + print("flashing jungle") + # flash jungles + pjs = PandaJungle.list() + assert set(pjs) == set(JUNGLES), f"Got different jungles than expected:\ngot {set(pjs)}\nexpected {set(JUNGLES)}" + for s in pjs: + with PandaJungle(serial=s) as pj: + print(f"- flashing {s}") + pj.flash() + r.cycle_power(delay=0, ports=[1, 2]) r.close() diff --git a/tests/debug_console.py b/tests/debug_console.py index a74cd846e8..8755be1498 100755 --- a/tests/debug_console.py +++ b/tests/debug_console.py @@ -6,8 +6,7 @@ import select import codecs -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda setcolor = ["\033[1;32;40m", "\033[1;31;40m"] unsetcolor = "\033[00m" @@ -24,7 +23,7 @@ if os.getenv("SERIAL"): serials = [x for x in serials if x == os.getenv("SERIAL")] - pandas = list([Panda(x, claim=claim) for x in serials]) + pandas = [Panda(x, claim=claim) for x in serials] decoders = [codecs.getincrementaldecoder('utf-8')() for _ in pandas] if not len(pandas): diff --git a/tests/echo.py b/tests/echo.py index e7cf3e5b3c..90bf4a8c76 100755 --- a/tests/echo.py +++ b/tests/echo.py @@ -1,9 +1,5 @@ #!/usr/bin/env python3 -import os -import sys - -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +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". diff --git a/tests/elm_car_simulator.py b/tests/elm_car_simulator.py index 8c8360a1e7..27afdbad48 100755 --- a/tests/elm_car_simulator.py +++ b/tests/elm_car_simulator.py @@ -1,18 +1,16 @@ #!/usr/bin/env python3 -# flake8: noqa """Used to Reverse/Test ELM protocol auto detect and OBD message response without a car.""" -import sys import os +import sys import struct import binascii import time import threading from collections import deque -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def lin_checksum(dat): return sum(dat) % 0x100 @@ -100,8 +98,10 @@ def __lin_monitor(self): print("Invalid bytes at start of message") print(" BUFF", lin_buff) continue - if len(lin_buff) < msglen + 4: continue - if lin_checksum(lin_buff[:-1]) != lin_buff[-1]: continue + if len(lin_buff) < msglen + 4: + continue + if lin_checksum(lin_buff[:-1]) != lin_buff[-1]: + continue self.__lin_process_msg(lin_buff[0] & 0xF8, # Priority lin_buff[1], lin_buff[2], lin_buff[3:-1]) lin_buff = bytearray() diff --git a/tests/gmlan_harness_test.py b/tests/gmlan_harness_test.py index f10f98fc3d..950918cff2 100755 --- a/tests/gmlan_harness_test.py +++ b/tests/gmlan_harness_test.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 -import os -import sys import time -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda WHITE_GMLAN_BUS = 3 OTHER_GMLAN_BUS = 1 diff --git a/tests/gps_stability_test.py b/tests/gps_stability_test.py deleted file mode 100755 index f8cb13caab..0000000000 --- a/tests/gps_stability_test.py +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env python3 -# flake8: noqa - -import os -import sys -import time -import random -import threading - -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda, PandaSerial # noqa: E402 - -INIT_GPS_BAUD = 9600 -GPS_BAUD = 460800 - -def connect(): - pandas = Panda.list() - print(pandas) - - # make sure two pandas are connected - if len(pandas) != 2: - print("Connect white and grey/black panda to run this test!") - assert False - - # connect - pandas[0] = Panda(pandas[0]) - pandas[1] = Panda(pandas[1]) - - white_panda = None - gps_panda = None - - # find out which one is white (for spamming the CAN buses) - if pandas[0].get_type() == Panda.HW_TYPE_WHITE_PANDA and pandas[1].get_type() != Panda.HW_TYPE_WHITE_PANDA: - white_panda = pandas[0] - gps_panda = pandas[1] - elif pandas[0].get_type() != Panda.HW_TYPE_WHITE_PANDA and pandas[1].get_type() == Panda.HW_TYPE_WHITE_PANDA: - white_panda = pandas[1] - gps_panda = pandas[0] - else: - print("Connect white and grey/black panda to run this test!") - assert False - return white_panda, gps_panda - -def spam_buses_thread(panda): - try: - panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) - while True: - at = random.randint(1, 2000) - st = (b"test" + os.urandom(10))[0:8] - bus = random.randint(0, 2) - panda.can_send(at, st, bus) - except Exception as e: - print(e) - -def read_can_thread(panda): - try: - while True: - panda.can_recv() - except Exception as e: - print(e) - -def init_gps(panda): - def add_nmea_checksum(msg): - d = msg[1:] - cs = 0 - for i in d: - cs ^= ord(i) - return msg + "*%02X" % cs - - ser = PandaSerial(panda, 1, INIT_GPS_BAUD) - - # Power cycle the gps by toggling reset - print("Resetting GPS") - panda.set_esp_power(0) - time.sleep(0.5) - panda.set_esp_power(1) - time.sleep(0.5) - - # Upping baud rate - print("Upping GPS baud rate") - msg = str.encode(add_nmea_checksum("$PUBX,41,1,0007,0003,%d,0" % GPS_BAUD) + "\r\n") - ser.write(msg) - time.sleep(1) # needs a wait for it to actually send - - # Reconnecting with the correct baud - ser = PandaSerial(panda, 1, GPS_BAUD) - - # Sending all config messages boardd sends - print("Sending config") - ser.write(b"\xB5\x62\x06\x00\x14\x00\x03\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\x1E\x7F") - ser.write(b"\xB5\x62\x06\x3E\x00\x00\x44\xD2") - ser.write(b"\xB5\x62\x06\x00\x14\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x35") - ser.write(b"\xB5\x62\x06\x00\x14\x00\x01\x00\x00\x00\xC0\x08\x00\x00\x00\x08\x07\x00\x01\x00\x01\x00\x00\x00\x00\x00\xF4\x80") - ser.write(b"\xB5\x62\x06\x00\x14\x00\x04\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1D\x85") - ser.write(b"\xB5\x62\x06\x00\x00\x00\x06\x18") - ser.write(b"\xB5\x62\x06\x00\x01\x00\x01\x08\x22") - ser.write(b"\xB5\x62\x06\x00\x01\x00\x02\x09\x23") - ser.write(b"\xB5\x62\x06\x00\x01\x00\x03\x0A\x24") - ser.write(b"\xB5\x62\x06\x08\x06\x00\x64\x00\x01\x00\x00\x00\x79\x10") - ser.write(b"\xB5\x62\x06\x24\x24\x00\x05\x00\x04\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5A\x63") - ser.write(b"\xB5\x62\x06\x1E\x14\x00\x00\x00\x00\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3C\x37") - ser.write(b"\xB5\x62\x06\x24\x00\x00\x2A\x84") - ser.write(b"\xB5\x62\x06\x23\x00\x00\x29\x81") - ser.write(b"\xB5\x62\x06\x1E\x00\x00\x24\x72") - ser.write(b"\xB5\x62\x06\x01\x03\x00\x01\x07\x01\x13\x51") - ser.write(b"\xB5\x62\x06\x01\x03\x00\x02\x15\x01\x22\x70") - ser.write(b"\xB5\x62\x06\x01\x03\x00\x02\x13\x01\x20\x6C") - - print("Initialized GPS") - -received_messages = 0 -received_bytes = 0 -send_something = False -def gps_read_thread(panda): - global received_messages, received_bytes, send_something - ser = PandaSerial(panda, 1, GPS_BAUD) - while True: - ret = ser.read(1024) - time.sleep(0.001) - if len(ret): - received_messages += 1 - received_bytes += len(ret) - if send_something: - ser.write("test") - send_something = False - - -CHECK_PERIOD = 5 -MIN_BYTES = 10000 -MAX_BYTES = 50000 - -min_failures = 0 -max_failures = 0 - -if __name__ == "__main__": - white_panda, gps_panda = connect() - - # Start spamming the CAN buses with the white panda. Also read the messages to add load on the GPS panda - threading.Thread(target=spam_buses_thread, args=(white_panda,)).start() - threading.Thread(target=read_can_thread, args=(gps_panda,)).start() - - # Start GPS checking - init_gps(gps_panda) - - read_thread = threading.Thread(target=gps_read_thread, args=(gps_panda,)) - read_thread.start() - while True: - time.sleep(CHECK_PERIOD) - if(received_bytes < MIN_BYTES): - print("Panda is not sending out enough data! Got " + str(received_messages) + " (" + str(received_bytes) + "B) in the last " + str(CHECK_PERIOD) + " seconds") - send_something = True - min_failures += 1 - elif(received_bytes > MAX_BYTES): - print("Panda is not sending out too much data! Got " + str(received_messages) + " (" + str(received_bytes) + "B) in the last " + str(CHECK_PERIOD) + " seconds") - print("Probably not on the right baud rate, got reset somehow? Resetting...") - max_failures += 1 - init_gps(gps_panda) - else: - print("Got " + str(received_messages) + " (" + str(received_bytes) + "B) messages in the last " + str(CHECK_PERIOD) + " seconds.") - if(min_failures > 0): - print("Total min failures: ", min_failures) - if(max_failures > 0): - print("Total max failures: ", max_failures) - received_messages = 0 - received_bytes = 0 diff --git a/tests/hitl/0_dfu.py b/tests/hitl/0_dfu.py deleted file mode 100644 index f3b8ae04aa..0000000000 --- a/tests/hitl/0_dfu.py +++ /dev/null @@ -1,45 +0,0 @@ -import pytest -import random - -from panda import Panda, PandaDFU -from panda.python.spi import SpiDevice - -@pytest.mark.expected_logs(1) -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=20), "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() - - -@pytest.mark.expected_logs(1) -@pytest.mark.test_panda_types((Panda.HW_TYPE_TRES, )) -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=20), "failed to enter DFU" - - # send junk - for _ in range(10): - speed = 1000000 * random.randint(1, 5) - d = SpiDevice(speed=speed) - with d.acquire() as spi: - dat = [random.randint(0, 255) for _ in range(random.randint(1, 100))] - spi.xfer(dat) - - # should still show up - assert dfu_serial in PandaDFU.list() diff --git a/tests/hitl/1_program.py b/tests/hitl/1_program.py index b051636f5e..d4e6e4fd5d 100644 --- a/tests/hitl/1_program.py +++ b/tests/hitl/1_program.py @@ -9,11 +9,30 @@ def check_signature(p): assert not p.bootstub, "Flashed firmware not booting. Stuck in bootstub." assert p.up_to_date() + +@pytest.mark.expected_logs(1) +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.execution_timeout(30) @pytest.mark.expected_logs(1, 2) -def test_a_known_bootstub(p): +def test_known_bootstub(p): """ Test that compiled app can work with known production bootstub """ @@ -60,13 +79,13 @@ def test_a_known_bootstub(p): @pytest.mark.execution_timeout(25) @pytest.mark.expected_logs(1) -def test_b_recover(p): +def test_recover(p): assert p.recover(timeout=30) check_signature(p) @pytest.mark.execution_timeout(25) @pytest.mark.expected_logs(3) -def test_c_flash(p): +def test_flash(p): # test flash from bootstub serial = p._serial assert serial is not None diff --git a/tests/hitl/2_health.py b/tests/hitl/2_health.py index 1fe0b5a1fb..56d52b46cc 100644 --- a/tests/hitl/2_health.py +++ b/tests/hitl/2_health.py @@ -2,7 +2,7 @@ import pytest from panda import Panda -from panda_jungle import PandaJungle # pylint: disable=import-error +from panda import PandaJungle from panda.tests.hitl.conftest import PandaGroup @pytest.mark.expected_logs(1) diff --git a/tests/hitl/3_usb_to_can.py b/tests/hitl/3_usb_to_can.py index 22cfde438e..9321eb4e85 100644 --- a/tests/hitl/3_usb_to_can.py +++ b/tests/hitl/3_usb_to_can.py @@ -1,4 +1,3 @@ -import sys import time import pytest from flaky import flaky @@ -60,9 +59,6 @@ def test_reliability(p): et = (time.monotonic() - st) * 1000.0 assert et < 20 - sys.stdout.write("P") - sys.stdout.flush() - @flaky(max_runs=6, min_passes=1) def test_throughput(p): # enable output mode @@ -90,10 +86,6 @@ def test_gmlan(p): p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) p.set_can_loopback(True) - p.set_can_speed_kbps(1, SPEED_NORMAL) - p.set_can_speed_kbps(2, SPEED_NORMAL) - p.set_can_speed_kbps(3, SPEED_GMLAN) - # set gmlan on CAN2 for bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3, Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]: p.set_gmlan(bus) diff --git a/tests/hitl/4_can_loopback.py b/tests/hitl/4_can_loopback.py index f7e8bb640d..28986f58b8 100644 --- a/tests/hitl/4_can_loopback.py +++ b/tests/hitl/4_can_loopback.py @@ -7,30 +7,17 @@ from collections import defaultdict from panda import Panda -from panda.tests.hitl.conftest import PandaGroup, PARTIAL_TESTS -from panda.tests.hitl.helpers import time_many_sends, clear_can_buffers +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.execution_timeout(35) def test_send_recv(p, panda_jungle): def test(p_send, p_recv): - p_send.set_can_loopback(False) - p_recv.set_can_loopback(False) - - p_send.can_send_many([(0x1ba, 0, b"message", 0)] * 2) - time.sleep(0.05) - p_recv.can_recv() - p_send.can_recv() - for bus in (0, 1, 2): for speed in (10, 20, 50, 100, 125, 250, 500, 1000): - p_send.set_can_speed_kbps(bus, speed) - p_recv.set_can_speed_kbps(bus, speed) - time.sleep(0.1) - - clear_can_buffers(p_send) - clear_can_buffers(p_recv) - time.sleep(0.1) + 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) @@ -49,27 +36,10 @@ def test(p_send, p_recv): @pytest.mark.execution_timeout(30) def test_latency(p, panda_jungle): def test(p_send, p_recv): - p_send.set_can_loopback(False) - p_recv.set_can_loopback(False) - - p_send.set_can_speed_kbps(0, 500) - p_recv.set_can_speed_kbps(0, 500) - time.sleep(0.05) - - p_send.can_send_many([(0x1ba, 0, b"testmsg", 0)] * 10) - time.sleep(0.05) - p_recv.can_recv() - p_send.can_recv() - for bus in (0, 1, 2): for speed in (10, 20, 50, 100, 125, 250, 500, 1000): - p_send.set_can_speed_kbps(bus, speed) - p_recv.set_can_speed_kbps(bus, speed) - time.sleep(0.1) - - # clear can buffers - clear_can_buffers(p_send) - clear_can_buffers(p_recv) + clear_can_buffers(p_send, speed) + clear_can_buffers(p_recv, speed) latencies = [] comp_kbps_list = [] @@ -165,11 +135,11 @@ def test(p_send, p_recv, address=None): test(p, panda_jungle, 0x18DB33F1) test(panda_jungle, p, 0x18DB33F1) -def test_bulk_write(p, panda_jungle): - # TODO: doesn't work in partial test mode - if PARTIAL_TESTS: - return + # 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 @@ -204,27 +174,15 @@ def flood_tx(panda): if len(rx) != 4 * NUM_MESSAGES_PER_BUS: raise Exception("Did not receive all messages!") - # Set back to silent mode - p.set_safety_mode(Panda.SAFETY_SILENT) - def test_message_integrity(p): - clear_can_buffers(p) - p.set_safety_mode(Panda.SAFETY_ALLOUTPUT) - p.set_can_loopback(True) - - n = 250 - for i in range(n): + for i in range(250): sent_msgs = defaultdict(set) for _ in range(random.randrange(10)): - to_send = [] - for __ in range(random.randrange(100)): - bus = random.randrange(3) - addr = random.randrange(1, 1<<29) - dat = bytes([random.getrandbits(8) for _ in range(random.randrange(1, 9))]) - sent_msgs[bus].add((addr, dat)) - to_send.append([addr, None, dat, bus]) + 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() @@ -233,13 +191,12 @@ def test_message_integrity(p): for msg in recvd: if msg[3] >= 128: k = (msg[0], bytes(msg[2])) - assert k in sent_msgs[msg[3]-128], f"message {k} was never sent on bus {bus}" + 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" - # Set back to silent mode - p.set_safety_mode(Panda.SAFETY_SILENT) print("Got all messages intact") diff --git a/tests/hitl/5_gps.py b/tests/hitl/5_gps.py deleted file mode 100644 index 3cead333ed..0000000000 --- a/tests/hitl/5_gps.py +++ /dev/null @@ -1,21 +0,0 @@ -import time -import pytest - -from panda import PandaSerial -from panda.tests.hitl.conftest import PandaGroup - - -@pytest.mark.test_panda_types(PandaGroup.GPS) -def test_gps_version(p): - serial = PandaSerial(p, 1, 9600) - # Reset and check twice to make sure the enabling works - for _ in range(2): - # Reset GPS - p.set_esp_power(0) - time.sleep(2) - p.set_esp_power(1) - time.sleep(1) - - # Read startup message and check if version is contained - dat = serial.read(0x1000) # Read one full panda DMA buffer. This should include the startup message - assert b'HPG 1.40ROV' in dat \ No newline at end of file diff --git a/tests/hitl/8_spi.py b/tests/hitl/5_spi.py similarity index 78% rename from tests/hitl/8_spi.py rename to tests/hitl/5_spi.py index d609446d5d..d67f7c4ea5 100644 --- a/tests/hitl/8_spi.py +++ b/tests/hitl/5_spi.py @@ -3,13 +3,33 @@ import random from unittest.mock import patch -from panda import Panda -from panda.python.spi import PandaProtocolMismatch, PandaSpiNackResponse +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") +@pytest.mark.expected_logs(0) +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 diff --git a/tests/hitl/7_internal.py b/tests/hitl/7_internal.py index d4e792721c..9717a24959 100644 --- a/tests/hitl/7_internal.py +++ b/tests/hitl/7_internal.py @@ -49,7 +49,7 @@ def test_fan_cooldown(p): def test_fan_overshoot(p): if p.get_type() == Panda.HW_TYPE_DOS: - pytest.skip("fan controller overshoots on fans that need stall recovery") + 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) diff --git a/tests/hitl/conftest.py b/tests/hitl/conftest.py index 8f4d405b9a..6deb2e0eee 100644 --- a/tests/hitl/conftest.py +++ b/tests/hitl/conftest.py @@ -1,41 +1,38 @@ -import concurrent.futures import os -import time import pytest +import concurrent.futures -from panda import Panda, PandaDFU +from panda import Panda, PandaDFU, PandaJungle from panda.tests.hitl.helpers import clear_can_buffers -NO_JUNGLE = os.environ.get("NO_JUNGLE", "0") == "1" -if not NO_JUNGLE: - from panda_jungle import PandaJungle # pylint: disable=import-error - +# 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)] -PEDAL_SERIAL = 'none' JUNGLE_SERIAL = os.getenv("PANDAS_JUNGLE") +NO_JUNGLE = os.environ.get("NO_JUNGLE", "0") == "1" PANDAS_EXCLUDE = os.getenv("PANDAS_EXCLUDE", "").strip().split(" ") -PARTIAL_TESTS = os.environ.get("PARTIAL_TESTS", "0") == "1" 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 - GPS = (Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_UNO) 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 PARTIAL_TESTS: - # minimal set of pandas to get most of our coverage - # * red panda covers GEN2, STM32H7 - # * black panda covers STM32F4, GEN2, and GPS - PandaGroup.TESTED = (Panda.HW_TYPE_BLACK_PANDA, Panda.HW_TYPE_RED_PANDA) # type: ignore -elif HW_TYPES is not None: +if HW_TYPES is not None: PandaGroup.TESTED = [bytes([int(x), ]) for x in HW_TYPES.strip().split(",")] # type: ignore @@ -49,8 +46,8 @@ def init_all_pandas(): _panda_jungle.set_panda_power(True) for serial in Panda.list(): - if serial not in PANDAS_EXCLUDE and serial != PEDAL_SERIAL: - with Panda(serial=serial) as p: + 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 @@ -61,7 +58,7 @@ def init_all_pandas(): print(f"{len(_all_pandas)} total pandas") init_all_pandas() -_all_panda_serials = list(_all_pandas.keys()) +_all_panda_serials = sorted(_all_pandas.keys()) def init_jungle(): @@ -75,6 +72,9 @@ def init_jungle(): 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( @@ -90,6 +90,7 @@ def pytest_configure(config): "markers", "expected_logs(amount, ...): mark test to expect a certain amount of panda logs" ) +@pytest.hookimpl(tryfirst=True) def pytest_collection_modifyitems(items): for item in items: if item.get_closest_marker('execution_timeout') is None: @@ -98,6 +99,16 @@ def pytest_collection_modifyitems(items): item.add_marker(pytest.mark.setup_timeout(20)) item.add_marker(pytest.mark.teardown_timeout(20)) + # 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: @@ -107,12 +118,12 @@ def pytest_make_parametrize_id(config, val, argname): return None -@pytest.fixture(name='panda_jungle') +@pytest.fixture(name='panda_jungle', scope='function') def fixture_panda_jungle(request): init_jungle() return _panda_jungle -@pytest.fixture(name='p') +@pytest.fixture(name='p', scope='function') def func_fixture_panda(request, module_panda): p = module_panda @@ -205,12 +216,6 @@ def fixture_panda_setup(request): # Initialize jungle init_jungle() - # wait for all pandas to come up - for _ in range(50): - if set(_all_panda_serials).issubset(set(Panda.list())): - break - time.sleep(0.1) - # Connect to pandas def cnnct(s): if s == panda_serial: @@ -219,14 +224,13 @@ def cnnct(s): p.set_can_loopback(False) p.set_gmlan(None) - p.set_esp_power(False) 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 - else: + elif not PARALLEL: with Panda(serial=s) as p: p.reset(reconnect=False) return None diff --git a/tests/hitl/helpers.py b/tests/hitl/helpers.py index ee04784042..d7ac4da0d6 100644 --- a/tests/hitl/helpers.py +++ b/tests/hitl/helpers.py @@ -1,5 +1,6 @@ import time import random +from typing import Optional def get_random_can_messages(n): @@ -12,14 +13,14 @@ def get_random_can_messages(n): return m -def time_many_sends(p, bus, p_recv=None, msg_count=100, two_pandas=False): +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" * 8, bus)] * msg_count + to_send = [(msg_id, 0, b"\xaa" * msg_len, bus)] * msg_count start_time = time.monotonic() p.can_send_many(to_send) @@ -46,12 +47,16 @@ def time_many_sends(p, bus, p_recv=None, msg_count=100, two_pandas=False): assert len(sent_echo) == msg_count end_time = (end_time - start_time) * 1000.0 - comp_kbps = (1 + 11 + 1 + 1 + 1 + 4 + 8 * 8 + 15 + 1 + 1 + 1 + 7) * msg_count / end_time + 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): +def clear_can_buffers(panda, speed: Optional[int] = 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) @@ -64,5 +69,4 @@ def clear_can_buffers(panda): r = panda.can_recv() time.sleep(0.05) if (time.monotonic() - st) > 10: - print("Unable to clear can buffers for panda ", panda.get_serial()) - assert False + raise Exception("Unable to clear can buffers for panda ", panda.get_serial()) diff --git a/tests/hitl/run_parallel_tests.sh b/tests/hitl/run_parallel_tests.sh new file mode 100755 index 0000000000..b6b79d99f6 --- /dev/null +++ b/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/tests/hitl/test.sh b/tests/hitl/run_serial_tests.sh similarity index 67% rename from tests/hitl/test.sh rename to tests/hitl/run_serial_tests.sh index 7382ac5988..31270f044c 100755 --- a/tests/hitl/test.sh +++ b/tests/hitl/run_serial_tests.sh @@ -4,4 +4,4 @@ set -e DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" cd $DIR -pytest --durations=0 --maxfail=1 *.py +NON_PARALLEL=1 pytest --durations=0 *.py -x diff --git a/tests/ir_test.py b/tests/ir_test.py index 8566f37715..e41decf255 100755 --- a/tests/ir_test.py +++ b/tests/ir_test.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 -import os -import sys import time -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda power = 0 if __name__ == "__main__": diff --git a/tests/libpanda/SConscript b/tests/libpanda/SConscript index f9f052e458..265fac9d69 100644 --- a/tests/libpanda/SConscript +++ b/tests/libpanda/SConscript @@ -3,9 +3,9 @@ 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 + # 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 += '-12' + CC += '-13' env = Environment( CC=CC, @@ -14,6 +14,7 @@ env = Environment( '-fno-builtin', '-std=gnu11', '-Wfatal-errors', + '-Wno-pointer-to-int-cast', ], CPPPATH=[".", "../../board/"], ) diff --git a/tests/libpanda/safety_helpers.h b/tests/libpanda/safety_helpers.h index 5409168a2e..22a3c4f13a 100644 --- a/tests/libpanda/safety_helpers.h +++ b/tests/libpanda/safety_helpers.h @@ -145,6 +145,10 @@ void set_desired_angle_last(int t){ desired_angle_last = t; } +int get_desired_angle_last(void){ + return desired_angle_last; +} + int get_angle_meas_min(void){ return angle_meas.min; } diff --git a/tests/libpanda/safety_helpers.py b/tests/libpanda/safety_helpers.py index 252fbf3cb8..d10a82b004 100644 --- a/tests/libpanda/safety_helpers.py +++ b/tests/libpanda/safety_helpers.py @@ -31,6 +31,7 @@ def setup_safety_helpers(ffi): 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(); int get_angle_meas_min(void); int get_angle_meas_max(void); @@ -71,15 +72,16 @@ def get_vehicle_speed_max(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: ... # pylint: disable=redefined-builtin + 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: ... # pylint: disable=redefined-builtin + 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 get_angle_meas_min(self) -> int: ... def get_angle_meas_max(self) -> int: ... diff --git a/tests/location_listener.py b/tests/location_listener.py deleted file mode 100755 index d997327ead..0000000000 --- a/tests/location_listener.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -import os -import time -import sys - -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda, PandaSerial # noqa: 402 - -def add_nmea_checksum(msg): - d = msg[1:] - cs = 0 - for i in d: - cs ^= ord(i) - return msg + "*%02X" % cs - -if __name__ == "__main__": - panda = Panda() - ser = PandaSerial(panda, 1, 9600) - - # power cycle by toggling reset - print("resetting") - panda.set_esp_power(0) - time.sleep(0.5) - panda.set_esp_power(1) - time.sleep(0.5) - print("done") - print(ser.read(1024)) - - # upping baud rate - baudrate = 460800 - - print("upping baud rate") - msg = str.encode(add_nmea_checksum("$PUBX,41,1,0007,0003,%d,0" % baudrate) + "\r\n") - print(msg) - ser.write(msg) - time.sleep(0.1) # needs a wait for it to actually send - - # new panda serial - ser = PandaSerial(panda, 1, baudrate) - - while True: - ret = ser.read(1024) - if len(ret) > 0: - sys.stdout.write(ret.decode('ascii', 'ignore')) - sys.stdout.flush() diff --git a/tests/loopback_test.py b/tests/loopback_test.py index 84925952ae..73bb91be71 100755 --- a/tests/loopback_test.py +++ b/tests/loopback_test.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import os -import sys import time import random import argparse @@ -9,8 +8,7 @@ from hexdump import hexdump from itertools import permutations -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) @@ -20,13 +18,12 @@ def run_test(sleep_duration): print(pandas) if len(pandas) < 2: - print("Minimum two pandas are needed for test") - assert False + 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 = list([Panda(x) for x in pandas]) + h = [Panda(x) for x in pandas] print("H", h) for hh in h: @@ -116,5 +113,5 @@ def run_test_w_pandas(pandas, sleep_duration): while True: run_test(sleep_duration=args.sleep) else: - for i in range(args.n): + for _ in range(args.n): run_test(sleep_duration=args.sleep) diff --git a/tests/message_drop_test.py b/tests/message_drop_test.py index 31c6f8cbd1..99d35b043d 100755 --- a/tests/message_drop_test.py +++ b/tests/message_drop_test.py @@ -11,12 +11,12 @@ JUNGLE = "JUNGLE" in os.environ if JUNGLE: - from panda_jungle import PandaJungle # pylint: disable=import-error + 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(map(lambda msg: [[0xaa, None, msg, 0], [0xaa, None, msg, 1], [0xaa, None, msg, 2]], messages))) +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!') @@ -65,6 +65,6 @@ def flood_tx(panda): # Check if we received everything for bus in range(3): - received_msgs = set(map(lambda m: bytes(m[2]), filter(lambda m, b=bus: m[3] == b, rx))) # type: ignore + 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/tests/pedal/enter_canloader.py b/tests/pedal/enter_canloader.py index bdd0d62865..7465c0a072 100755 --- a/tests/pedal/enter_canloader.py +++ b/tests/pedal/enter_canloader.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import time import argparse -from panda import Panda, McuType -from panda.tests.pedal.canhandle import CanHandle +from panda import Panda, CanHandle, McuType if __name__ == "__main__": diff --git a/tests/pedal/test_pedal.py b/tests/pedal/test_pedal.py index 05a0512267..0d0676c45a 100755 --- a/tests/pedal/test_pedal.py +++ b/tests/pedal/test_pedal.py @@ -1,15 +1,12 @@ #!/usr/bin/env python3 import os import time -import subprocess import unittest -from panda import Panda, BASEDIR -from panda_jungle import PandaJungle # pylint: disable=import-error -from panda.tests.pedal.canhandle import CanHandle + +from panda import Panda, PandaJungle, CanHandle, McuType, BASEDIR JUNGLE_SERIAL = os.getenv("PEDAL_JUNGLE") -PEDAL_SERIAL = 'none' PEDAL_BUS = 1 class TestPedal(unittest.TestCase): @@ -30,7 +27,7 @@ def _flash_over_can(self, bus, fw_file): time.sleep(0.1) with open(fw_file, "rb") as code: - PandaJungle.flash_static(CanHandle(self.jungle, bus), code.read()) + PandaJungle.flash_static(CanHandle(self.jungle, bus), code.read(), McuType.F2) def _listen_can_frames(self): self.jungle.can_clear(0xFFFF) @@ -45,16 +42,14 @@ def _listen_can_frames(self): return msgs def test_usb_fw(self): - subprocess.check_output(f"cd {BASEDIR} && PEDAL=1 PEDAL_USB=1 scons", shell=True) - self._flash_over_can(PEDAL_BUS, f"{BASEDIR}board/obj/pedal_usb.bin.signed") + self._flash_over_can(PEDAL_BUS, f"{BASEDIR}/board/pedal/obj/pedal_usb.bin.signed") time.sleep(2) - with Panda(PEDAL_SERIAL) as p: + with Panda('pedal') as p: self.assertTrue(p.get_type() == Panda.HW_TYPE_PEDAL) self.assertTrue(self._listen_can_frames() > 40) def test_nonusb_fw(self): - subprocess.check_output(f"cd {BASEDIR} && PEDAL=1 scons", shell=True) - self._flash_over_can(PEDAL_BUS, f"{BASEDIR}board/obj/pedal.bin.signed") + self._flash_over_can(PEDAL_BUS, f"{BASEDIR}board/pedal/obj/pedal.bin.signed") time.sleep(2) self.assertTrue(self._listen_can_frames() > 40) diff --git a/tests/rtc_test.py b/tests/rtc_test.py index 24edf0976e..01c9f4ddd8 100755 --- a/tests/rtc_test.py +++ b/tests/rtc_test.py @@ -1,10 +1,7 @@ #!/usr/bin/env python -import os -import sys import datetime -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda if __name__ == "__main__": p = Panda() diff --git a/tests/safety/common.py b/tests/safety/common.py index 1b141d85e6..3bead37982 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -3,7 +3,7 @@ import unittest import importlib import numpy as np -from typing import Dict, List, Optional +from typing import Callable, Dict, List, Optional from opendbc.can.packer import CANPacker # pylint: disable=import-error from panda import ALTERNATIVE_EXPERIENCE @@ -12,6 +12,7 @@ MAX_WRONG_COUNTERS = 5 VEHICLE_SPEED_FACTOR = 100 +MessageFunction = Callable[[float], libpanda_py.CANPacket] def sign_of(a): return 1 if a > 0 else -1 @@ -53,6 +54,8 @@ def _regen_test(self): class PandaSafetyTestBase(unittest.TestCase): + safety: libpanda_py.Panda + @classmethod def setUpClass(cls): if cls.__name__ == "PandaSafetyTestBase": @@ -69,6 +72,32 @@ def _rx(self, 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: Optional[Callable[[float], 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)) + class InterceptorSafetyTest(PandaSafetyTestBase): @@ -175,6 +204,39 @@ def test_accel_actuation_limits(self, stock_longitudinal=False): self.assertEqual(should_tx, self._tx(self._accel_msg(accel))) +class LongitudinalGasBrakeSafetyTest(PandaSafetyTestBase, abc.ABC): + + MIN_BRAKE: int = 0 + MAX_BRAKE: Optional[int] = None + MAX_POSSIBLE_BRAKE: Optional[int] = None + + MIN_GAS: int = 0 + MAX_GAS: Optional[int] = None + INACTIVE_GAS = 0 + MAX_POSSIBLE_GAS: Optional[int] = 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 @@ -537,11 +599,46 @@ def test_reset_torque_measurements(self): self.assertEqual(self.safety.get_torque_meas_min(), 0) self.assertEqual(self.safety.get_torque_meas_max(), 0) +class MeasurementSafetyTest(PandaSafetyTestBase): + DEG_TO_CAN: float = 1 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "MeasurementSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _angle_meas_msg(self, angle: float): + pass + + @abc.abstractmethod + def _speed_msg(self, speed): + pass + + def common_measurement_test(self, msg_func, min_value, max_value, factor, get_min_func, get_max_func): + for val in np.arange(min_value, max_value, 0.5): + for i in range(6): + self.assertTrue(self._rx(msg_func(val + i * 0.1))) -class AngleSteeringSafetyTest(PandaSafetyTestBase): + # assert close by one decimal place + self.assertLessEqual(abs(get_min_func() - val * factor), 1 * abs(factor)) + self.assertLessEqual(abs(get_max_func() - (val + 0.5) * factor), 1 * abs(factor)) + + # reset sample_t by reinitializing the safety mode + self._reset_safety_hooks() + + self.assertEqual(get_min_func(), 0) + self.assertEqual(get_max_func(), 0) + + 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) - DEG_TO_CAN: int + def test_steering_angle_measurements(self): + self.common_measurement_test(self._angle_meas_msg, -180, 180, self.DEG_TO_CAN, self.safety.get_angle_meas_min, self.safety.get_angle_meas_max) + +class AngleSteeringSafetyTest(MeasurementSafetyTest): ANGLE_RATE_BP: List[float] ANGLE_RATE_UP: List[float] # windup limit ANGLE_RATE_DOWN: List[float] # unwind limit @@ -552,18 +649,10 @@ def setUpClass(cls): 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 = int(t * self.DEG_TO_CAN) self.safety.set_desired_angle_last(t) @@ -640,37 +729,6 @@ def test_angle_cmd_when_disabled(self): 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))) - def test_reset_angle_measurements(self): - # Tests that the angle measurement sample_t is reset on safety mode init - for a in np.linspace(-90, 90, 6): - self.assertTrue(self._rx(self._angle_meas_msg(a))) - - # reset sample_t by reinitializing the safety mode - self._reset_safety_hooks() - - self.assertEqual(self.safety.get_angle_meas_min(), 0) - self.assertEqual(self.safety.get_angle_meas_max(), 0) - - def test_vehicle_speed_measurements(self): - """ - Tests: - - rx hook correctly parses and rounds the vehicle speed - - sample is reset on safety mode init - """ - for speed in np.arange(0, 80, 0.5): - for i in range(6): - self.assertTrue(self._rx(self._speed_msg(speed + i * 0.1))) - - # assert close by one decimal place - self.assertLessEqual(abs(self.safety.get_vehicle_speed_min() - speed * VEHICLE_SPEED_FACTOR), 1) - self.assertLessEqual(abs(self.safety.get_vehicle_speed_max() - (speed + 0.5) * VEHICLE_SPEED_FACTOR), 1) - - # reset sample_t by reinitializing the safety mode - self._reset_safety_hooks() - - self.assertEqual(self.safety.get_vehicle_speed_min(), 0) - self.assertEqual(self.safety.get_vehicle_speed_max(), 0) - @add_regen_tests class PandaSafetyTest(PandaSafetyTestBase): @@ -888,14 +946,16 @@ def test_tx_hook_on_wrong_safety_mode(self): test = importlib.import_module("panda.tests.safety."+tf[:-3]) for attr in dir(test): if attr.startswith("Test") and attr != current_test: - tx = getattr(getattr(test, attr), "TX_MSGS") + 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 if attr.startswith('TestToyota') and current_test.startswith('TestToyota'): continue - if {attr, current_test}.issubset({'TestSubaruGen1Safety', 'TestSubaruGen2Safety'}): + if {attr, current_test}.issubset({'TestSubaruGen1TorqueStockLongitudinalSafety', 'TestSubaruGen2TorqueStockLongitudinalSafety', + 'TestSubaruGen1LongitudinalSafety', 'TestSubaruGen2LongitudinalSafety'}): continue if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}): continue @@ -928,7 +988,7 @@ def test_tx_hook_on_wrong_safety_mode(self): if attr.startswith('TestHonda'): # exceptions for common msgs across different hondas tx = list(filter(lambda m: m[0] not in [0x1FA, 0x30C, 0x33D], tx)) - all_tx.append(list([m[0], m[1], attr] for m in 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) diff --git a/tests/safety/test.sh b/tests/safety/test.sh index 70164ec54e..1c427ee2ca 100755 --- a/tests/safety/test.sh +++ b/tests/safety/test.sh @@ -1,10 +1,7 @@ #!/usr/bin/env bash - -# Loop over all HW_TYPEs, see board/boards/board_declarations.h -# Make sure test fails if one HW_TYPE fails set -e -HW_TYPES=( 6 7 9 ) +HW_TYPES=( 6 9 ) for hw_type in "${HW_TYPES[@]}"; do echo "Testing HW_TYPE: $hw_type" HW_TYPE=$hw_type python -m unittest discover . diff --git a/tests/safety/test_chrysler.py b/tests/safety/test_chrysler.py index b9390f5536..77bc129557 100755 --- a/tests/safety/test_chrysler.py +++ b/tests/safety/test_chrysler.py @@ -7,11 +7,11 @@ class TestChryslerSafety(common.PandaSafetyTest, common.MotorTorqueSteeringSafetyTest): - TX_MSGS = [[571, 0], [658, 0], [678, 0]] + TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0]] STANDSTILL_THRESHOLD = 0 RELAY_MALFUNCTION_ADDR = 0x292 RELAY_MALFUNCTION_BUS = 0 - FWD_BLACKLISTED_ADDRS = {2: [658, 678]} + FWD_BLACKLISTED_ADDRS = {2: [0x292, 0x2A6]} FWD_BUS_LOOKUP = {0: 2, 2: 0} MAX_RATE_UP = 3 @@ -21,6 +21,8 @@ class TestChryslerSafety(common.PandaSafetyTest, common.MotorTorqueSteeringSafet RT_INTERVAL = 250000 MAX_TORQUE_ERROR = 80 + LKAS_ACTIVE_VALUE = 1 + DAS_BUS = 0 def setUp(self): @@ -54,7 +56,7 @@ def _torque_meas_msg(self, 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} + 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): @@ -73,9 +75,9 @@ def test_buttons(self): class TestChryslerRamDTSafety(TestChryslerSafety): - TX_MSGS = [[177, 2], [166, 0], [250, 0]] - RELAY_MALFUNCTION_ADDR = 166 - FWD_BLACKLISTED_ADDRS = {2: [166, 250]} + TX_MSGS = [[0xB1, 2], [0xA6, 0], [0xFA, 0]] + RELAY_MALFUNCTION_ADDR = 0xA6 + FWD_BLACKLISTED_ADDRS = {2: [0xA6, 0xFA]} MAX_RATE_UP = 6 MAX_RATE_DOWN = 6 @@ -83,6 +85,8 @@ class TestChryslerRamDTSafety(TestChryslerSafety): DAS_BUS = 2 + LKAS_ACTIVE_VALUE = 2 + def setUp(self): self.packer = CANPackerPanda("chrysler_ram_dt_generated") self.safety = libpanda_py.libpanda @@ -94,9 +98,9 @@ def _speed_msg(self, speed): return self.packer.make_can_msg_panda("ESP_8", 0, values) class TestChryslerRamHDSafety(TestChryslerSafety): - TX_MSGS = [[629, 0], [630, 0], [570, 2]] - RELAY_MALFUNCTION_ADDR = 630 - FWD_BLACKLISTED_ADDRS = {2: [629, 630]} + TX_MSGS = [[0x275, 0], [0x276, 0], [0x23A, 2]] + RELAY_MALFUNCTION_ADDR = 0x276 + FWD_BLACKLISTED_ADDRS = {2: [0x275, 0x276]} MAX_TORQUE = 361 MAX_RATE_UP = 14 @@ -105,6 +109,8 @@ class TestChryslerRamHDSafety(TestChryslerSafety): DAS_BUS = 2 + LKAS_ACTIVE_VALUE = 2 + def setUp(self): self.packer = CANPackerPanda("chrysler_ram_hd_generated") self.safety = libpanda_py.libpanda diff --git a/tests/safety/test_gm.py b/tests/safety/test_gm.py index f6b604ad31..f05aa8674f 100755 --- a/tests/safety/test_gm.py +++ b/tests/safety/test_gm.py @@ -14,11 +14,37 @@ class Buttons: CANCEL = 6 -class GmLongitudinalBase(common.PandaSafetyTest): +class GmLongitudinalBase(common.PandaSafetyTest, common.LongitudinalGasBrakeSafetyTest): # pylint: disable=no-member,abstract-method + 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 PandaSafetyTest, 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. @@ -41,23 +67,10 @@ def test_cancel_button(self): self._rx(self._button_msg(Buttons.CANCEL)) self.assertFalse(self.safety.get_controls_allowed()) - # override these tests from PandaSafetyTest, GM longitudinal uses 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 _pcm_status_msg(self, enable): - pass - class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): STANDSTILL_THRESHOLD = 10 * 0.0311 - RELAY_MALFUNCTION_ADDR = 384 + RELAY_MALFUNCTION_ADDR = 0x180 RELAY_MALFUNCTION_BUS = 0 BUTTONS_BUS = 0 # rx or tx BRAKE_BUS = 0 # tx only @@ -70,11 +83,6 @@ class TestGmSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafety DRIVER_TORQUE_ALLOWANCE = 65 DRIVER_TORQUE_FACTOR = 4 - MAX_GAS = 0 - MAX_REGEN = 0 - INACTIVE_REGEN = 0 - MAX_BRAKE = 0 - PCM_CRUISE = True # openpilot is tied to the PCM state if not longitudinal @classmethod @@ -118,58 +126,31 @@ def _user_gas_msg(self, gas): values["CruiseState"] = self.safety.get_controls_allowed() return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) - 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) - def _torque_driver_msg(self, torque): values = {"LKADriverAppldTrq": torque} return self.packer.make_can_msg_panda("PSCMStatus", 0, values) def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKASteeringCmd": torque} + 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) - def test_brake_safety_check(self): - for enabled in [0, 1]: - for b in range(0, 500): - self.safety.set_controls_allowed(enabled) - if abs(b) > self.MAX_BRAKE or (not enabled and b != 0): - self.assertFalse(self._tx(self._send_brake_msg(b))) - else: - self.assertTrue(self._tx(self._send_brake_msg(b))) - - def test_gas_safety_check(self): - # Block if enabled and out of actuation range, disabled and not inactive regen, or if stock longitudinal - for enabled in [0, 1]: - for gas_regen in range(0, 2 ** 12 - 1): - self.safety.set_controls_allowed(enabled) - should_tx = ((enabled and self.MAX_REGEN <= gas_regen <= self.MAX_GAS) or - gas_regen == self.INACTIVE_REGEN) - self.assertEqual(should_tx, self._tx(self._send_gas_msg(gas_regen)), (enabled, gas_regen)) - class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase): - TX_MSGS = [[384, 0], [1033, 0], [1034, 0], [715, 0], [880, 0], # pt bus - [161, 1], [774, 1], [776, 1], [784, 1], # obs bus - [789, 2], # ch bus + 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 - MAX_REGEN = 1404 - INACTIVE_REGEN = 1404 - MAX_BRAKE = 400 + MIN_GAS = 1404 # maximum regen + INACTIVE_GAS = 1404 def setUp(self): self.packer = CANPackerPanda("gm_global_a_powertrain_generated") @@ -196,9 +177,9 @@ def _user_brake_msg(self, brake): class TestGmCameraSafety(TestGmCameraSafetyBase): - TX_MSGS = [[384, 0], # pt bus - [388, 2]] # camera bus - FWD_BLACKLISTED_ADDRS = {2: [384], 0: [388]} # block LKAS message and PSCMStatus + 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): @@ -222,24 +203,16 @@ def test_buttons(self): self._rx(self._pcm_status_msg(enabled)) self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) - # GM Cam safety mode does not allow longitudinal messages - def test_brake_safety_check(self): - pass - - def test_gas_safety_check(self): - pass - class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase): - TX_MSGS = [[384, 0], [789, 0], [715, 0], [880, 0], # pt bus - [388, 2]] # camera bus - FWD_BLACKLISTED_ADDRS = {2: [384, 715, 880, 789], 0: [388]} # block LKAS, ACC messages and PSCMStatus + 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 - MAX_REGEN = 1514 - INACTIVE_REGEN = 1554 - MAX_BRAKE = 400 + MIN_GAS = 1514 # maximum regen + INACTIVE_GAS = 1554 def setUp(self): self.packer = CANPackerPanda("gm_global_a_powertrain_generated") diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index f8e17c24e2..0be536cee8 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -64,18 +64,24 @@ def test_set_resume_buttons(self): """ Both SET and RES should enter controls allowed on their falling edge. """ - for btn in (Btn.SET, Btn.RESUME): - for main_on in (True, False): - self._rx(self._acc_state_msg(main_on)) - self.safety.set_controls_allowed(0) + 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()) - # nothing until falling edge - for _ in range(10): - self._rx(self._button_msg(btn, main_on=main_on)) - 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.NONE, main_on=main_on)) - self.assertEqual(main_on, self.safety.get_controls_allowed(), msg=f"{main_on=} {btn=}") + self._rx(self._button_msg(btn_cur)) + self.assertEqual(should_enable, self.safety.get_controls_allowed(), msg=f"{main_on=} {btn_prev=} {btn_cur=}") def test_main_cancel_buttons(self): """ @@ -132,7 +138,7 @@ def test_rx_hook(self): self.assertFalse(self.safety.get_controls_allowed()) # restore counters for future tests with a couple of good messages - for i in range(2): + for _ in range(2): self.safety.set_controls_allowed(1) self._rx(self._button_msg(Btn.SET)) self._rx(self._speed_msg(0)) diff --git a/tests/safety/test_hyundai.py b/tests/safety/test_hyundai.py index d57cadebff..17aee771e2 100755 --- a/tests/safety/test_hyundai.py +++ b/tests/safety/test_hyundai.py @@ -13,7 +13,7 @@ def checksum(msg): addr, t, dat, bus = msg chksum = 0 - if addr == 902: + if addr == 0x386: for i, b in enumerate(dat): for j in range(8): # exclude checksum and counter bits @@ -28,26 +28,26 @@ def checksum(msg): ret[7] |= (chksum & 0xc) << 4 else: for i, b in enumerate(dat): - if addr in [608, 1057] and i == 7: - b &= 0x0F if addr == 1057 else 0xF0 - elif addr == 916 and i == 6: + 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 == 916 and i == 7: + elif addr == 0x394 and i == 7: continue chksum += sum(divmod(b, 16)) chksum = (16 - chksum) % 16 ret = bytearray(dat) - ret[6 if addr == 916 else 7] |= chksum << (4 if addr == 1057 else 0) + ret[6 if addr == 0x394 else 7] |= chksum << (4 if addr == 0x421 else 0) return addr, t, ret, bus class TestHyundaiSafety(HyundaiButtonBase, common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): - TX_MSGS = [[832, 0], [1265, 0], [1157, 0]] - STANDSTILL_THRESHOLD = 30 # ~1kph - RELAY_MALFUNCTION_ADDR = 832 + TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0]] + STANDSTILL_THRESHOLD = 12 # 0.375 kph + RELAY_MALFUNCTION_ADDR = 0x340 RELAY_MALFUNCTION_BUS = 0 - FWD_BLACKLISTED_ADDRS = {2: [832, 1157]} + FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485]} FWD_BUS_LOOKUP = {0: 2, 2: 0} MAX_RATE_UP = 3 @@ -168,11 +168,10 @@ def _user_gas_msg(self, gas): return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) class TestHyundaiLongitudinalSafety(HyundaiLongitudinalBase, TestHyundaiSafety): - TX_MSGS = [[832, 0], [1265, 0], [1157, 0], [1056, 0], [1057, 0], [1290, 0], [905, 0], [1186, 0], [909, 0], [1155, 0], [2000, 0]] + TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0], [0x38D, 0], [0x483, 0], [0x7D0, 0]] - - DISABLED_ECU_UDS_MSG = (2000, 0) - DISABLED_ECU_ACTUATION_MSG = (1057, 0) + DISABLED_ECU_UDS_MSG = (0x7D0, 0) + DISABLED_ECU_ACTUATION_MSG = (0x421, 0) def setUp(self): self.packer = CANPackerPanda("hyundai_kia_generic") diff --git a/tests/safety/test_hyundai_canfd.py b/tests/safety/test_hyundai_canfd.py index 39b3845ba7..b1f4f895cb 100755 --- a/tests/safety/test_hyundai_canfd.py +++ b/tests/safety/test_hyundai_canfd.py @@ -12,7 +12,7 @@ class TestHyundaiCanfdBase(HyundaiButtonBase, common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]] - STANDSTILL_THRESHOLD = 30 # ~1kph + STANDSTILL_THRESHOLD = 12 # 0.375 kph RELAY_MALFUNCTION_ADDR = 0x50 RELAY_MALFUNCTION_BUS = 0 FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} @@ -180,6 +180,28 @@ def setUp(self): 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_ADDR = 0x110 + RELAY_MALFUNCTION_BUS = 0 + 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], diff --git a/tests/safety/test_subaru.py b/tests/safety/test_subaru.py index f633ef193d..5c54cc1e07 100755 --- a/tests/safety/test_subaru.py +++ b/tests/safety/test_subaru.py @@ -1,48 +1,64 @@ #!/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 - - -MSG_SUBARU_Brake_Status = 0x13c -MSG_SUBARU_CruiseControl = 0x240 -MSG_SUBARU_Throttle = 0x40 -MSG_SUBARU_Steering_Torque = 0x119 -MSG_SUBARU_Wheel_Speeds = 0x13a -MSG_SUBARU_ES_LKAS = 0x122 -MSG_SUBARU_ES_Brake = 0x220 -MSG_SUBARU_ES_Distance = 0x221 -MSG_SUBARU_ES_Status = 0x222 -MSG_SUBARU_ES_DashStatus = 0x321 -MSG_SUBARU_ES_LKAS_State = 0x322 -MSG_SUBARU_ES_Infotainment = 0x323 +from panda.tests.safety.common import CANPackerPanda, MeasurementSafetyTest +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): - return [[MSG_SUBARU_ES_LKAS, SUBARU_MAIN_BUS], - [MSG_SUBARU_ES_Distance, alt_bus], - [MSG_SUBARU_ES_DashStatus, SUBARU_MAIN_BUS], - [MSG_SUBARU_ES_LKAS_State, SUBARU_MAIN_BUS], - [MSG_SUBARU_ES_Infotainment, SUBARU_MAIN_BUS]] +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]] -class TestSubaruSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): +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.PandaSafetyTest, MeasurementSafetyTest): FLAGS = 0 STANDSTILL_THRESHOLD = 0 # kph - RELAY_MALFUNCTION_ADDR = MSG_SUBARU_ES_LKAS + RELAY_MALFUNCTION_ADDR = SubaruMsg.ES_LKAS RELAY_MALFUNCTION_BUS = SUBARU_MAIN_BUS FWD_BUS_LOOKUP = {SUBARU_MAIN_BUS: SUBARU_CAM_BUS, SUBARU_CAM_BUS: SUBARU_MAIN_BUS} - FWD_BLACKLISTED_ADDRS = {SUBARU_CAM_BUS: [MSG_SUBARU_ES_LKAS, MSG_SUBARU_ES_DashStatus, MSG_SUBARU_ES_LKAS_State, MSG_SUBARU_ES_Infotainment]} - - MAX_RATE_UP = 50 - MAX_RATE_DOWN = 70 - MAX_TORQUE = 2047 + FWD_BLACKLISTED_ADDRS = fwd_blacklisted_addr() MAX_RT_DELTA = 940 RT_INTERVAL = 250000 @@ -50,7 +66,12 @@ class TestSubaruSafetyBase(common.PandaSafetyTest, common.DriverTorqueSteeringSa DRIVER_TORQUE_ALLOWANCE = 60 DRIVER_TORQUE_FACTOR = 50 - ALT_BUS = SUBARU_MAIN_BUS + 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") @@ -68,17 +89,16 @@ def _torque_driver_msg(self, 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.057 for s in ["FR", "FL", "RR", "RL"]} - return self.packer.make_can_msg_panda("Wheel_Speeds", self.ALT_BUS, values) + 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_BUS, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKAS_Output": torque} - return self.packer.make_can_msg_panda("ES_LKAS", 0, values) + return self.packer.make_can_msg_panda("Brake_Status", self.ALT_MAIN_BUS, values) def _user_gas_msg(self, gas): values = {"Throttle_Pedal": gas} @@ -86,25 +106,120 @@ def _user_gas_msg(self, gas): def _pcm_status_msg(self, enable): values = {"Cruise_Activated": enable} - return self.packer.make_can_msg_panda("CruiseControl", self.ALT_BUS, values) + return self.packer.make_can_msg_panda("CruiseControl", self.ALT_MAIN_BUS, values) -class TestSubaruGen2SafetyBase(TestSubaruSafetyBase): - ALT_BUS = SUBARU_ALT_BUS +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**12 + + MIN_BRAKE = 0 + MAX_BRAKE = 600 + MAX_POSSIBLE_BRAKE = 2**16 + + MIN_RPM = 0 + MAX_RPM = 2400 + MAX_POSSIBLE_RPM = 2**12 + + 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) - MAX_RATE_UP = 40 - MAX_RATE_DOWN = 40 - MAX_TORQUE = 1000 -class TestSubaruGen1Safety(TestSubaruSafetyBase): +class TestSubaruTorqueSafetyBase(TestSubaruSafetyBase, common.DriverTorqueSteeringSafetyTest): + MAX_RATE_UP = 50 + MAX_RATE_DOWN = 70 + MAX_TORQUE = 2047 + + 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 TestSubaruGen2Safety(TestSubaruGen2SafetyBase): +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/tests/safety/test_subaru_preglobal.py b/tests/safety/test_subaru_preglobal.py index 40362e3728..5e20dc5039 100755 --- a/tests/safety/test_subaru_preglobal.py +++ b/tests/safety/test_subaru_preglobal.py @@ -49,7 +49,7 @@ def _user_brake_msg(self, 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} + 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): diff --git a/tests/safety/test_volkswagen_mqb.py b/tests/safety/test_volkswagen_mqb.py index 0a49d477b0..8eeb4dadc2 100755 --- a/tests/safety/test_volkswagen_mqb.py +++ b/tests/safety/test_volkswagen_mqb.py @@ -85,7 +85,7 @@ def _torque_driver_msg(self, torque): # 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} + 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 diff --git a/tests/safety_replay/replay_drive.py b/tests/safety_replay/replay_drive.py index 77ee61d5d8..8e8b64ab0e 100755 --- a/tests/safety_replay/replay_drive.py +++ b/tests/safety_replay/replay_drive.py @@ -72,8 +72,8 @@ def replay_drive(lr, safety_mode, param, alternative_experience, segment=False): return tx_controls_blocked == 0 and rx_invalid == 0 and not safety_tick_rx_invalid if __name__ == "__main__": - from tools.lib.route import Route, SegmentName - from tools.lib.logreader import MultiLogIterator # pylint: disable=import-error + from openpilot.tools.lib.route import Route, SegmentName + from openpilot.tools.lib.logreader import MultiLogIterator # pylint: disable=import-error parser = argparse.ArgumentParser(description="Replay CAN messages from a route or segment through a safety mode", formatter_class=argparse.ArgumentDefaultsHelpFormatter) diff --git a/tests/safety_replay/test_safety_replay.py b/tests/safety_replay/test_safety_replay.py deleted file mode 100755 index 2dcc07cd1a..0000000000 --- a/tests/safety_replay/test_safety_replay.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -from collections import namedtuple -import os -import requests - -from panda import Panda -from panda.python import ALTERNATIVE_EXPERIENCE as ALT_EXP -from panda.tests.safety_replay.replay_drive import replay_drive -from tools.lib.logreader import LogReader # pylint: disable=import-error - -BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" - -ReplayRoute = namedtuple("ReplayRoute", ("route", "safety_mode", "param", "alternative_experience"), defaults=(0, 0)) - -logs = [ - ReplayRoute("2425568437959f9d|2019-12-22--16-24-37.bz2", Panda.SAFETY_HONDA_NIDEC), # HONDA.CIVIC (fcw presents: 0x1FA blocked as expected) - ReplayRoute("38bfd238edecbcd7|2019-06-07--10-15-25.bz2", Panda.SAFETY_TOYOTA, 66), # TOYOTA.PRIUS - ReplayRoute("f89c604cf653e2bf|2018-09-29--13-46-50.bz2", Panda.SAFETY_GM), # GM.VOLT - ReplayRoute("6fb4948a7ebe670e|2019-11-12--00-35-53.bz2", Panda.SAFETY_CHRYSLER), # CHRYSLER.PACIFICA_2018_HYBRID - ReplayRoute("0744286ead2fbb96|2023-05-01--16-27-01--35--rlog.bz2", Panda.SAFETY_SUBARU), # SUBARU.IMPREZA - ReplayRoute("bad6ae3584ece5b5|2023-04-29--11-23-48--7--rlog.bz2", Panda.SAFETY_SUBARU, # SUBARU.OUTBACK - Panda.FLAG_SUBARU_GEN2, ALT_EXP.DISABLE_DISENGAGE_ON_GAS), - ReplayRoute("76b83eb0245de90e|2020-03-05--19-16-05.bz2", Panda.SAFETY_VOLKSWAGEN_MQB), # VOLKSWAGEN.GOLF (stock ACC) - ReplayRoute("3cfdec54aa035f3f|2022-10-13--14-58-58.bz2", Panda.SAFETY_VOLKSWAGEN_MQB, # VOLKSWAGEN.GOLF (openpilot long) - Panda.FLAG_VOLKSWAGEN_LONG_CONTROL), - ReplayRoute("3cfdec54aa035f3f|2022-07-19--23-45-10.bz2", Panda.SAFETY_VOLKSWAGEN_PQ, # VOLKSWAGEN.PASSAT_NMS (openpilot longitudinal) - Panda.FLAG_VOLKSWAGEN_LONG_CONTROL), - ReplayRoute("fbbfa6af821552b9|2020-03-03--08-09-43.bz2", Panda.SAFETY_NISSAN), # NISSAN.XTRAIL - ReplayRoute("5b7c365c50084530_2020-04-15--16-13-24--3--rlog.bz2", Panda.SAFETY_HYUNDAI), # HYUNDAI.SONATA - ReplayRoute("610ebb9faaad6b43|2020-06-13--15-28-36.bz2", Panda.SAFETY_HYUNDAI_LEGACY), # HYUNDAI.IONIQ_EV_LTD - ReplayRoute("5ab784f361e19b78_2020-06-08--16-30-41.bz2", Panda.SAFETY_SUBARU_PREGLOBAL), # SUBARU.OUTBACK_PREGLOBAL - ReplayRoute("bb50caf5f0945ab1|2021-06-19--17-20-18.bz2", Panda.SAFETY_TESLA), # TESLA.AP2_MODELS - ReplayRoute("bd6a637565e91581_2021-10-29--22-18-31--1--rlog.bz2", Panda.SAFETY_MAZDA), # MAZDA.CX9_2021 - ReplayRoute("1a5d045d2c531a6d_2022-06-07--22-03-00--1--rlog.bz2", Panda.SAFETY_HONDA_BOSCH, # HONDA.CIVIC_2022 - Panda.FLAG_HONDA_RADARLESS, ALT_EXP.DISABLE_DISENGAGE_ON_GAS), -] - - -if __name__ == "__main__": - - # get all the routes - for route, _, _, _ in logs: - if not os.path.isfile(route): - with open(route, "wb") as f: - r = requests.get(BASE_URL + route, timeout=10) - r.raise_for_status() - f.write(r.content) - - failed = [] - for route, mode, param, alt_exp in logs: - lr = LogReader(route, sort_by_time=True) - - print("\nreplaying %s with safety mode %d, param %s, alternative experience %s" % (route, mode, param, alt_exp)) - if not replay_drive(lr, mode, param, alt_exp): - failed.append(route) - - for f in failed: - print(f"\n**** failed on {f} ****") - assert len(failed) == 0, "\nfailed on %d logs" % len(failed) diff --git a/tests/setup_device_ci.sh b/tests/setup_device_ci.sh index e6a8c048a7..e876b94375 100755 --- a/tests/setup_device_ci.sh +++ b/tests/setup_device_ci.sh @@ -50,16 +50,6 @@ if [ ! -d "$SOURCE_DIR" ]; then git clone https://github.com/commaai/panda.git $SOURCE_DIR fi -# setup panda_jungle -cd $SOURCE_DIR/../ -if [ ! -d panda_jungle/ ]; then - git clone https://github.com/commaai/panda_jungle.git -fi -cd panda_jungle -git fetch --all -git checkout -f master -git reset --hard origin/master - # setup device/SOM state SOM_ST_IO=49 echo $SOM_ST_IO > /sys/class/gpio/export || true diff --git a/tests/spam_can.py b/tests/spam_can.py index 8c80fb45cb..3154cc0463 100755 --- a/tests/spam_can.py +++ b/tests/spam_can.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 import os -import sys import random -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) diff --git a/tests/standalone_test.py b/tests/standalone_test.py index 6580b06d14..7ec5559697 100755 --- a/tests/standalone_test.py +++ b/tests/standalone_test.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 -import os -import sys import struct import time -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda if __name__ == "__main__": p = Panda() @@ -13,7 +10,7 @@ print(p.health()) t1 = time.time() - for i in range(100): + for _ in range(100): p.get_serial() t2 = time.time() print("100 requests took %.2f ms" % ((t2 - t1) * 1000)) diff --git a/tests/tucan_loopback.py b/tests/tucan_loopback.py index ff037f3e35..1bf1254a16 100755 --- a/tests/tucan_loopback.py +++ b/tests/tucan_loopback.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import os -import sys import time import random import argparse @@ -9,8 +8,7 @@ from hexdump import hexdump from itertools import permutations -sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")) -from panda import Panda # noqa: E402 +from panda import Panda def get_test_string(): return b"test" + os.urandom(10) @@ -20,13 +18,12 @@ def run_test(sleep_duration): print(pandas) if len(pandas) < 2: - print("Two pandas are needed for test") - assert False + raise Exception("Two pandas are needed for test") run_test_w_pandas(pandas, sleep_duration) def run_test_w_pandas(pandas, sleep_duration): - h = list([Panda(x) for x in pandas]) + h = [Panda(x) for x in pandas] print("H", h) for hh in h: @@ -114,5 +111,5 @@ def run_test_w_pandas(pandas, sleep_duration): while True: run_test(sleep_duration=args.sleep) else: - for i in range(args.n): + for _ in range(args.n): run_test(sleep_duration=args.sleep)