From 08c95bf47ba6d0c5d11a7616e42e10b8dbde19eb Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 26 Sep 2024 19:37:59 -0700 Subject: [PATCH 01/33] mutation tests: activate all mutation ops (#2047) * all * clean * cleaner * WORKING * test * cleanup --- tests/safety/mutation.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/safety/mutation.sh b/tests/safety/mutation.sh index 23bba67c74..e8c242cab3 100755 --- a/tests/safety/mutation.sh +++ b/tests/safety/mutation.sh @@ -6,10 +6,11 @@ cd $DIR $DIR/install_mull.sh -scons --mutation -j$(nproc) -D - GIT_REF="${GIT_REF:-origin/master}" -echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: ../../" > mull.yml +GIT_ROOT=$(git rev-parse --show-toplevel) +echo -e "mutators:\n - cxx_all" > $GIT_ROOT/mull.yml +scons --mutation -j$(nproc) -D +echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: $GIT_ROOT" >> $GIT_ROOT/mull.yml SAFETY_MODELS=$(find * | grep "^test_.*\.py") for safety_model in ${SAFETY_MODELS[@]}; do From b69d31cd29222178a47a2d1511c5c6d879d155ef Mon Sep 17 00:00:00 2001 From: commaci-public <60409688+commaci-public@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:14:12 -0700 Subject: [PATCH 02/33] pre-commit: autoupdate hooks (#2051) Update pre-commit hook versions Co-authored-by: adeebshihadeh --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c2759df9c3..623a67e24b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,6 @@ repos: additional_dependencies: ['numpy', 'types-requests', 'types-atomicwrites', 'types-pycurl'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.6.8 hooks: - id: ruff From f5e1900537b39b98ca269fd3d934bac792c4ae12 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Tue, 1 Oct 2024 19:05:39 -0700 Subject: [PATCH 03/33] mutation tests: remove `cxx_assign_const` op (#2053) * no 42 * last one --- tests/safety/mutation.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/safety/mutation.sh b/tests/safety/mutation.sh index e8c242cab3..9b40bdeddd 100755 --- a/tests/safety/mutation.sh +++ b/tests/safety/mutation.sh @@ -8,7 +8,8 @@ $DIR/install_mull.sh GIT_REF="${GIT_REF:-origin/master}" GIT_ROOT=$(git rev-parse --show-toplevel) -echo -e "mutators:\n - cxx_all" > $GIT_ROOT/mull.yml +MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic]" +echo -e "$MULL_OPS" > $GIT_ROOT/mull.yml scons --mutation -j$(nproc) -D echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: $GIT_ROOT" >> $GIT_ROOT/mull.yml From fab8d0bb4a7704d771bbac79e1cf94c6469f8888 Mon Sep 17 00:00:00 2001 From: Radek Date: Wed, 2 Oct 2024 20:53:32 +0100 Subject: [PATCH 04/33] CAN print improvement (#2052) * Jungle spam can. * Print all busses by default. Prettier print. * can_health printer * colors on can error --- board/jungle/scripts/can_health.py | 20 ++++++++++++++++++-- board/jungle/scripts/can_printer.py | 17 +++++++++++------ board/jungle/scripts/spam_can.py | 20 ++++++++++++++++++++ tests/can_health.py | 29 +++++++++++++++++++++++++++++ tests/can_printer.py | 20 ++++++++++---------- 5 files changed, 88 insertions(+), 18 deletions(-) create mode 100755 board/jungle/scripts/spam_can.py create mode 100755 tests/can_health.py diff --git a/board/jungle/scripts/can_health.py b/board/jungle/scripts/can_health.py index ff068b5baa..47efbb2e67 100755 --- a/board/jungle/scripts/can_health.py +++ b/board/jungle/scripts/can_health.py @@ -1,13 +1,29 @@ #!/usr/bin/env python3 - import time +import re from panda import PandaJungle +RED = '\033[91m' +GREEN = '\033[92m' + +def colorize_errors(value): + if isinstance(value, str): + if re.search(r'(?i)No error', value): + return f'{GREEN}{value}\033[0m' + elif re.search(r'(?i)(? 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) + for k, v in sorted(msgs.items()): + last_msg, last_src = v[-1] + dd += "%d: %s(%6d): %s\n" % (last_src, "%04X(%4d)" % (k, k), len(v), binascii.hexlify(last_msg).decode()) print(dd) lp = sec_since_boot() diff --git a/board/jungle/scripts/spam_can.py b/board/jungle/scripts/spam_can.py new file mode 100755 index 0000000000..c3d798b473 --- /dev/null +++ b/board/jungle/scripts/spam_can.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +import os +import random +from panda import PandaJungle + +def get_test_string(): + return b"test" + os.urandom(10) + +if __name__ == "__main__": + p = PandaJungle() + + p.set_safety_mode(PandaJungle.SAFETY_ALLOUTPUT) + + print("Spamming all buses...") + while True: + at = random.randint(1, 2000) + st = get_test_string()[0:8] + bus = random.randint(0, 2) + p.can_send(at, st, bus) + # print("Sent message on bus: ", bus) diff --git a/tests/can_health.py b/tests/can_health.py new file mode 100755 index 0000000000..26de9cdab1 --- /dev/null +++ b/tests/can_health.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +import time +import re +from panda import Panda + +RED = '\033[91m' +GREEN = '\033[92m' + +def colorize_errors(value): + if isinstance(value, str): + if re.search(r'(?i)No error', value): + return f'{GREEN}{value}\033[0m' + elif re.search(r'(?i)(? 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) + for k, v in sorted(msgs.items()): + last_msg, last_src = v[-1] + dd += "%d: %s(%6d): %s\n" % (last_src, "%04X(%4d)" % (k, k), len(v), binascii.hexlify(last_msg).decode()) print(dd) lp = sec_since_boot() From 47f84169425c181e9034800d8acb1c12e591f2e4 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 2 Oct 2024 21:19:17 -0700 Subject: [PATCH 05/33] mutation test: add more ops (#2059) * log * more --- tests/safety/mutation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/safety/mutation.sh b/tests/safety/mutation.sh index 9b40bdeddd..a3356c25a0 100755 --- a/tests/safety/mutation.sh +++ b/tests/safety/mutation.sh @@ -8,7 +8,7 @@ $DIR/install_mull.sh GIT_REF="${GIT_REF:-origin/master}" GIT_ROOT=$(git rev-parse --show-toplevel) -MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic]" +MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic, cxx_remove_negation, cxx_replace_scalar_call]" echo -e "$MULL_OPS" > $GIT_ROOT/mull.yml scons --mutation -j$(nproc) -D echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: $GIT_ROOT" >> $GIT_ROOT/mull.yml From 2fa1cf5eff1dbea14023ebb4cfb2ac9273090693 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 2 Oct 2024 21:27:40 -0700 Subject: [PATCH 06/33] mutation tests: remove buggy mutation op --- tests/safety/mutation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/safety/mutation.sh b/tests/safety/mutation.sh index a3356c25a0..f208a151ad 100755 --- a/tests/safety/mutation.sh +++ b/tests/safety/mutation.sh @@ -8,7 +8,7 @@ $DIR/install_mull.sh GIT_REF="${GIT_REF:-origin/master}" GIT_ROOT=$(git rev-parse --show-toplevel) -MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic, cxx_remove_negation, cxx_replace_scalar_call]" +MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic, cxx_remove_negation]" echo -e "$MULL_OPS" > $GIT_ROOT/mull.yml scons --mutation -j$(nproc) -D echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: $GIT_ROOT" >> $GIT_ROOT/mull.yml From c77d3832ba0c4e7ba72d089bbe978393c194367f Mon Sep 17 00:00:00 2001 From: dzid26 Date: Wed, 2 Oct 2024 17:05:15 -0700 Subject: [PATCH 07/33] use logger info instead of warning --- python/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/__init__.py b/python/__init__.py index d2bbb2fe45..3e310cafd2 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -515,22 +515,22 @@ def flash_static(handle, code, mcu_type): assert last_sector < 7, "Binary too large! Risk of overwriting provisioning chunk." # unlock flash - logger.warning("flash: unlocking") + logger.info("flash: unlocking") handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'') # erase sectors - logger.warning(f"flash: erasing sectors 1 - {last_sector}") + logger.info(f"flash: erasing sectors 1 - {last_sector}") for i in range(1, last_sector + 1): handle.controlWrite(Panda.REQUEST_IN, 0xb2, i, 0, b'') # flash over EP2 STEP = 0x10 - logger.warning("flash: flashing") + logger.info("flash: flashing") for i in range(0, len(code), STEP): handle.bulkWrite(2, code[i:i + STEP]) # reset - logger.warning("flash: resetting") + logger.info("flash: resetting") try: handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'', expect_disconnect=True) except Exception: @@ -538,7 +538,7 @@ def flash_static(handle, code, mcu_type): def flash(self, fn=None, code=None, reconnect=True): if self.up_to_date(fn=fn): - logger.debug("flash: already up to date") + logger.info("flash: already up to date") return if not fn: From abdc41856b62cc269e63374354f132dc168c28b4 Mon Sep 17 00:00:00 2001 From: Jason Young <46612682+jyoung8607@users.noreply.github.com> Date: Fri, 4 Oct 2024 11:22:18 -0700 Subject: [PATCH 08/33] Toyota: 2021+ RAV4 Prime (#2042) * from @pd0wm commaai/panda#1841 * diff reduction: won't need the flag rename * run tests with new DBC and new gas/brake messages * toyota_secoc_car to global * consolidate alt_brake_101 * consolidate alt_pcm_cruise_176 * consolidate alt_gas_pedal_116 * don't allow transmit/forward for 0x131 unless SecOC * cleanup and todo * diff reduction * reorder by usage frequency * test for no LTA actuation * bump opendbc commit ref in Dockerfile * gate SecOC variant on ALLOW_DEBUG * tweak gating for MISRA * mutation test hates lta_angle surviving various changes * common rx checks styling * don't allow short version of 0x2E5 for SecOC mode * whitespace diff reduction * secoc_car -> secoc * fix comment typo * retry CI * missed a couple secoc_car -> secoc * one big secoc/not-secoc block * genuinely useful MISRA warning * test both STEERING_LTA and STEERING_LTA_2 * comment labeling for STEERING_LTA_2 signals * Update board/safety/safety_toyota.h Co-authored-by: Shane Smiskol * STEERING_LTA and STEERING_LTA_2 consistency * update gas/brake/cruise signal annotations --------- Co-authored-by: Shane Smiskol --- Dockerfile | 2 +- board/safety/safety_toyota.h | 93 ++++++++++++++++++++++++++++-------- python/__init__.py | 1 + tests/safety/test_toyota.py | 37 +++++++++++++- 4 files changed, 110 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 670283dd47..68362f8cfd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN pip3 install --break-system-packages --no-cache-dir $PYTHONPATH/panda/[dev] # TODO: this should be a "pip install" or not even in this repo at all RUN git config --global --add safe.directory $PYTHONPATH/panda -ENV OPENDBC_REF="5ed7a834a4e0e24c3968dd1e98ceb4b9d5f9791a" +ENV OPENDBC_REF="e1ce3619a5db661ef2b406ccf258a253baf6eebc" RUN cd /tmp/ && \ git clone --depth 1 https://github.com/commaai/opendbc opendbc_repo && \ cd opendbc_repo && git fetch origin $OPENDBC_REF && git checkout FETCH_HEAD && rm -rf .git/ && \ diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h index 73fb730372..7008bf8419 100644 --- a/board/safety/safety_toyota.h +++ b/board/safety/safety_toyota.h @@ -3,8 +3,16 @@ #include "safety_declarations.h" // Stock longitudinal -#define TOYOTA_COMMON_TX_MSGS \ - {0x2E4, 0, 5}, {0x191, 0, 8}, {0x412, 0, 8}, {0x343, 0, 8}, {0x1D2, 0, 8}, /* LKAS + LTA + ACC & PCM cancel cmds */ \ +#define TOYOTA_BASE_TX_MSGS \ + {0x191, 0, 8}, {0x412, 0, 8}, {0x343, 0, 8}, {0x1D2, 0, 8}, /* LKAS + LTA + ACC & PCM cancel cmds */ \ + +#define TOYOTA_COMMON_TX_MSGS \ + TOYOTA_BASE_TX_MSGS \ + {0x2E4, 0, 5}, \ + +#define TOYOTA_COMMON_SECOC_TX_MSGS \ + TOYOTA_BASE_TX_MSGS \ + {0x2E4, 0, 8}, {0x131, 0, 8}, \ #define TOYOTA_COMMON_LONG_TX_MSGS \ TOYOTA_COMMON_TX_MSGS \ @@ -16,10 +24,13 @@ #define TOYOTA_COMMON_RX_CHECKS(lta) \ {.msg = {{ 0xaa, 0, 8, .check_checksum = false, .frequency = 83U}, { 0 }, { 0 }}}, \ {.msg = {{0x260, 0, 8, .check_checksum = true, .quality_flag = (lta), .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{0x1D2, 0, 8, .check_checksum = true, .frequency = 33U}, { 0 }, { 0 }}}, \ - {.msg = {{0x224, 0, 8, .check_checksum = false, .frequency = 40U}, \ - {0x226, 0, 8, .check_checksum = false, .frequency = 40U}, { 0 }}}, \ + {.msg = {{0x1D2, 0, 8, .check_checksum = true, .frequency = 33U}, \ + {0x176, 0, 8, .check_checksum = true, .frequency = 32U}, { 0 }}}, \ + {.msg = {{0x101, 0, 8, .check_checksum = false, .frequency = 50U}, \ + {0x224, 0, 8, .check_checksum = false, .frequency = 40U}, \ + {0x226, 0, 8, .check_checksum = false, .frequency = 40U}}}, \ +static bool toyota_secoc = false; static bool toyota_alt_brake = false; static bool toyota_stock_longitudinal = false; static bool toyota_lta = false; @@ -87,14 +98,31 @@ static void toyota_rx_hook(const CANPacket_t *to_push) { } // enter controls on rising edge of ACC, exit controls on ACC off - // exit controls on rising edge of gas press - if (addr == 0x1D2) { - // 5th bit is CRUISE_ACTIVE - bool cruise_engaged = GET_BIT(to_push, 5U); - pcm_cruise_check(cruise_engaged); - - // sample gas pedal - gas_pressed = !GET_BIT(to_push, 4U); + // exit controls on rising edge of gas press, if not alternative experience + // exit controls on rising edge of brake press + if (toyota_secoc) { + if (addr == 0x176) { + bool cruise_engaged = GET_BIT(to_push, 5U); // PCM_CRUISE.CRUISE_ACTIVE + pcm_cruise_check(cruise_engaged); + } + if (addr == 0x116) { + gas_pressed = GET_BYTE(to_push, 1) != 0U; // GAS_PEDAL.GAS_PEDAL_USER + } + if (addr == 0x101) { + brake_pressed = GET_BIT(to_push, 3U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_rav4_prime_generated.dbc) + } + } else { + if (addr == 0x1D2) { + bool cruise_engaged = GET_BIT(to_push, 5U); // PCM_CRUISE.CRUISE_ACTIVE + pcm_cruise_check(cruise_engaged); + gas_pressed = !GET_BIT(to_push, 4U); // PCM_CRUISE.GAS_RELEASED + } + if (!toyota_alt_brake && (addr == 0x226)) { + brake_pressed = GET_BIT(to_push, 37U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_nodsu_pt_generated.dbc) + } + if (toyota_alt_brake && (addr == 0x224)) { + brake_pressed = GET_BIT(to_push, 5U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_new_mc_pt_generated.dbc) + } } // sample speed @@ -111,12 +139,6 @@ static void toyota_rx_hook(const CANPacket_t *to_push) { UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 / 3.6); } - // most cars have brake_pressed on 0x226, corolla and rav4 on 0x224 - if (((addr == 0x224) && toyota_alt_brake) || ((addr == 0x226) && !toyota_alt_brake)) { - uint8_t bit = (addr == 0x224) ? 5U : 37U; - brake_pressed = GET_BIT(to_push, bit); - } - bool stock_ecu_detected = addr == 0x2E4; // STEERING_LKA if (!toyota_stock_longitudinal && (addr == 0x343)) { stock_ecu_detected = true; // ACC_CONTROL @@ -203,7 +225,7 @@ static bool toyota_tx_hook(const CANPacket_t *to_send) { } } - // LTA angle steering check + // STEERING_LTA angle steering check if (addr == 0x191) { // check the STEER_REQUEST, STEER_REQUEST_2, TORQUE_WIND_DOWN, STEER_ANGLE_CMD signals bool lta_request = GET_BIT(to_send, 0U); @@ -251,6 +273,20 @@ static bool toyota_tx_hook(const CANPacket_t *to_send) { } } + // STEERING_LTA_2 angle steering check (SecOC) + if (toyota_secoc && (addr == 0x131)) { + // SecOC cars block any form of LTA actuation for now + bool lta_request = GET_BIT(to_send, 3U); // STEERING_LTA_2.STEER_REQUEST + bool lta_request2 = GET_BIT(to_send, 0U); // STEERING_LTA_2.STEER_REQUEST_2 + int lta_angle_msb = GET_BYTE(to_send, 2); // STEERING_LTA_2.STEER_ANGLE_CMD (MSB) + int lta_angle_lsb = GET_BYTE(to_send, 3); // STEERING_LTA_2.STEER_ANGLE_CMD (LSB) + + bool actuation = lta_request || lta_request2 || (lta_angle_msb != 0) || (lta_angle_lsb != 0); + if (actuation) { + tx = false; + } + } + // STEER: safety check on bytes 2-3 if (addr == 0x2E4) { int desired_torque = (GET_BYTE(to_send, 1) << 8) | GET_BYTE(to_send, 2); @@ -286,6 +322,10 @@ static safety_config toyota_init(uint16_t param) { TOYOTA_COMMON_TX_MSGS }; + static const CanMsg TOYOTA_SECOC_TX_MSGS[] = { + TOYOTA_COMMON_SECOC_TX_MSGS + }; + static const CanMsg TOYOTA_LONG_TX_MSGS[] = { TOYOTA_COMMON_LONG_TX_MSGS }; @@ -298,6 +338,11 @@ static safety_config toyota_init(uint16_t param) { const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET; const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET; +#ifdef ALLOW_DEBUG + const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET; + toyota_secoc = GET_FLAG(param, TOYOTA_PARAM_SECOC); +#endif + toyota_alt_brake = GET_FLAG(param, TOYOTA_PARAM_ALT_BRAKE); toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL); toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA); @@ -305,7 +350,11 @@ static safety_config toyota_init(uint16_t param) { safety_config ret; if (toyota_stock_longitudinal) { - SET_TX_MSGS(TOYOTA_TX_MSGS, ret); + if (toyota_secoc) { + SET_TX_MSGS(TOYOTA_SECOC_TX_MSGS, ret); + } else { + SET_TX_MSGS(TOYOTA_TX_MSGS, ret); + } } else { SET_TX_MSGS(TOYOTA_LONG_TX_MSGS, ret); } @@ -340,6 +389,8 @@ static int toyota_fwd_hook(int bus_num, int addr) { // block stock lkas messages and stock acc messages (if OP is doing ACC) // in TSS2, 0x191 is LTA which we need to block to avoid controls collision bool is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191)); + // on SecOC cars 0x131 is also LTA + is_lkas_msg |= toyota_secoc && (addr == 0x131); // in TSS2 the camera does ACC as well, so filter 0x343 bool is_acc_msg = (addr == 0x343); bool block_msg = is_lkas_msg || (is_acc_msg && !toyota_stock_longitudinal); diff --git a/python/__init__.py b/python/__init__.py index 3e310cafd2..7da3648e2a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -187,6 +187,7 @@ class Panda: FLAG_TOYOTA_ALT_BRAKE = (1 << 8) FLAG_TOYOTA_STOCK_LONGITUDINAL = (2 << 8) FLAG_TOYOTA_LTA = (4 << 8) + FLAG_TOYOTA_SECOC = (8 << 8) FLAG_HONDA_ALT_BRAKE = 1 FLAG_HONDA_BOSCH_LONG = 2 diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py index 80bf9ce9a1..e60b29c5c2 100755 --- a/tests/safety/test_toyota.py +++ b/tests/safety/test_toyota.py @@ -10,6 +10,7 @@ from panda.tests.safety.common import CANPackerPanda TOYOTA_COMMON_TX_MSGS = [[0x2E4, 0], [0x191, 0], [0x412, 0], [0x343, 0], [0x1D2, 0]] # LKAS + LTA + ACC & PCM cancel cmds +TOYOTA_SECOC_TX_MSGS = [[0x131, 0]] + TOYOTA_COMMON_TX_MSGS TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0 [0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1 [0x411, 0], # PCS_HUD @@ -107,7 +108,8 @@ def test_lta_steer_cmd(self): self.safety.set_controls_allowed(engaged) should_tx = not req and not req2 and angle == 0 and torque_wind_down == 0 - self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) + self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down)), + f"{req=} {req2=} {angle=} {torque_wind_down=}") def test_rx_hook(self): # checksum checks @@ -324,5 +326,38 @@ def setUp(self): self.safety.init_tests() +class TestToyotaSecOcSafety(TestToyotaStockLongitudinalBase): + + TX_MSGS = TOYOTA_SECOC_TX_MSGS + RELAY_MALFUNCTION_ADDRS = {0: (0x2E4,)} + FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x131]} + + def setUp(self): + self.packer = CANPackerPanda("toyota_rav4_prime_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_TOYOTA, self.EPS_SCALE | Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL | Panda.FLAG_TOYOTA_SECOC) + self.safety.init_tests() + + # This platform also has alternate brake and PCM messages, but same naming in the DBC, so same packers work + + def _user_gas_msg(self, gas): + values = {"GAS_PEDAL_USER": gas} + return self.packer.make_can_msg_panda("GAS_PEDAL", 0, values) + + # This platform sends both STEERING_LTA (same as other Toyota) and STEERING_LTA_2 (SecOC signed) + # STEERING_LTA is checked for no-actuation by the base class, STEERING_LTA_2 is checked for no-actuation below + + def _lta_2_msg(self, req, req2, angle_cmd, torque_wind_down=100): + values = {"STEER_REQUEST": req, "STEER_REQUEST_2": req2, "STEER_ANGLE_CMD": angle_cmd} + return self.packer.make_can_msg_panda("STEERING_LTA_2", 0, values) + + def test_lta_2_steer_cmd(self): + for engaged, req, req2, angle in itertools.product([True, False], [0, 1], [0, 1], np.linspace(-20, 20, 5)): + self.safety.set_controls_allowed(engaged) + + should_tx = not req and not req2 and angle == 0 + self.assertEqual(should_tx, self._tx(self._lta_2_msg(req, req2, angle)), f"{req=} {req2=} {angle=}") + + if __name__ == "__main__": unittest.main() From e2c076cab86d2690bfd0e1f449a84eefa1a72e36 Mon Sep 17 00:00:00 2001 From: Lukas <61192133+lukasloetkolben@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:24:22 -0700 Subject: [PATCH 09/33] Remove retry can_send_many loop (#2060) * remove retry loop from can_send_many * while condition --- python/__init__.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/python/__init__.py b/python/__init__.py index 7da3648e2a..0cbf1ef94b 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -831,18 +831,10 @@ def can_reset_communications(self): @ensure_can_packet_version def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS): snds = pack_can_buffer(arr) - while True: - try: - for tx in snds: - while True: - bs = self._handle.bulkWrite(3, tx, timeout=timeout) - tx = tx[bs:] - if len(tx) == 0: - break - logger.error("CAN: PARTIAL SEND MANY, RETRYING") - break - except (usb1.USBErrorIO, usb1.USBErrorOverflow): - logger.error("CAN: BAD SEND MANY, RETRYING") + for tx in snds: + while len(tx) > 0: + bs = self._handle.bulkWrite(3, tx, timeout=timeout) + tx = tx[bs:] def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS): self.can_send_many([[addr, dat, bus]], timeout=timeout) From 2b9458e8a6333fea65141ce7515ca2d5fa6ffe93 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 18 Oct 2024 13:08:14 -0700 Subject: [PATCH 10/33] ci: run build_socketcan only on push to master and pull_request (#2063) push on master --- .github/workflows/drivers.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/drivers.yaml b/.github/workflows/drivers.yaml index 1af13d9196..b787e3a5e2 100644 --- a/.github/workflows/drivers.yaml +++ b/.github/workflows/drivers.yaml @@ -1,5 +1,9 @@ name: drivers -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: jobs: build_socketcan: From 9aec4294cc940964a5b2fa34aa83301ab408b377 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Fri, 18 Oct 2024 14:10:54 -0700 Subject: [PATCH 11/33] ci: test building in release mode (#2062) * build * space * better --- .github/workflows/test.yaml | 2 ++ board/safety/safety_chrysler.h | 50 +++++++++++++++++----------------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 12d956eecf..221bce0096 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -54,6 +54,8 @@ jobs: run: ${{ env.RUN }} "scons -j4 --ubsan" - name: Build jungle firmware with FINAL_PROVISIONING support run: ${{ env.RUN }} "FINAL_PROVISIONING=1 scons -j4 board/jungle" + - name: Build panda in release mode + run: ${{ env.RUN }} "CERT=certs/debug RELEASE=1 scons -j4" unit_tests: name: unit tests diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index 631dbecde9..2bbc942715 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -211,18 +211,6 @@ static safety_config chrysler_init(uint16_t param) { .CRUISE_BUTTONS = 0xB1, // Cruise control buttons }; - // CAN messages for the 5th gen RAM HD platform - static const ChryslerAddrs CHRYSLER_RAM_HD_ADDRS = { - .EPS_2 = 0x220, // EPS driver input torque - .ESP_1 = 0x140, // Brake pedal and vehicle speed - .ESP_8 = 0x11C, // Brake pedal and vehicle speed - .ECM_5 = 0x22F, // Throttle position sensor - .DAS_3 = 0x1F4, // ACC engagement states from DASM - .DAS_6 = 0x275, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 0x276, // LKAS controls from DASM - .CRUISE_BUTTONS = 0x23A, // Cruise control buttons - }; - static RxCheck chrysler_ram_dt_rx_checks[] = { {.msg = {{CHRYSLER_RAM_DT_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, {.msg = {{CHRYSLER_RAM_DT_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, @@ -240,14 +228,6 @@ static safety_config chrysler_init(uint16_t param) { {.msg = {{CHRYSLER_ADDRS.DAS_3, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, }; - static RxCheck chrysler_ram_hd_rx_checks[] = { - {.msg = {{CHRYSLER_RAM_HD_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - }; - static const CanMsg CHRYSLER_TX_MSGS[] = { {CHRYSLER_ADDRS.CRUISE_BUTTONS, 0, 3}, {CHRYSLER_ADDRS.LKAS_COMMAND, 0, 6}, @@ -260,21 +240,41 @@ static safety_config chrysler_init(uint16_t param) { {CHRYSLER_RAM_DT_ADDRS.DAS_6, 0, 8}, }; +#ifdef ALLOW_DEBUG + // CAN messages for the 5th gen RAM HD platform + static const ChryslerAddrs CHRYSLER_RAM_HD_ADDRS = { + .EPS_2 = 0x220, // EPS driver input torque + .ESP_1 = 0x140, // Brake pedal and vehicle speed + .ESP_8 = 0x11C, // Brake pedal and vehicle speed + .ECM_5 = 0x22F, // Throttle position sensor + .DAS_3 = 0x1F4, // ACC engagement states from DASM + .DAS_6 = 0x275, // LKAS HUD and auto headlight control from DASM + .LKAS_COMMAND = 0x276, // LKAS controls from DASM + .CRUISE_BUTTONS = 0x23A, // Cruise control buttons + }; + + static RxCheck chrysler_ram_hd_rx_checks[] = { + {.msg = {{CHRYSLER_RAM_HD_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + {.msg = {{CHRYSLER_RAM_HD_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, + }; + static const CanMsg CHRYSLER_RAM_HD_TX_MSGS[] = { {CHRYSLER_RAM_HD_ADDRS.CRUISE_BUTTONS, 2, 3}, {CHRYSLER_RAM_HD_ADDRS.LKAS_COMMAND, 0, 8}, {CHRYSLER_RAM_HD_ADDRS.DAS_6, 0, 8}, }; - safety_config ret; - - bool enable_ram_dt = GET_FLAG(param, CHRYSLER_PARAM_RAM_DT); - -#ifdef ALLOW_DEBUG const uint32_t CHRYSLER_PARAM_RAM_HD = 2U; // set for Ram HD platform bool enable_ram_hd = GET_FLAG(param, CHRYSLER_PARAM_RAM_HD); #endif + safety_config ret; + + bool enable_ram_dt = GET_FLAG(param, CHRYSLER_PARAM_RAM_DT); + if (enable_ram_dt) { chrysler_platform = CHRYSLER_RAM_DT; chrysler_addrs = &CHRYSLER_RAM_DT_ADDRS; From 8422a7437f8221175d4c8b857c676ed616cad7a5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 24 Oct 2024 13:27:49 -0700 Subject: [PATCH 12/33] python: open SPI device once (#2066) Co-authored-by: Comma Device --- python/spi.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/python/spi.py b/python/spi.py index 59e9018159..a6832beaed 100644 --- a/python/spi.py +++ b/python/spi.py @@ -70,8 +70,6 @@ class PandaSpiTransferFailed(PandaSpiException): pass -SPI_LOCK = threading.Lock() - class PandaSpiTransfer(ctypes.Structure): _fields_ = [ ('rx_buf', ctypes.c_uint64), @@ -83,6 +81,9 @@ class PandaSpiTransfer(ctypes.Structure): ('expect_disconnect', ctypes.c_uint8), ] + +SPI_LOCK = threading.Lock() +SPI_DEVICES = {} class SpiDevice: """ Provides locked, thread-safe access to a panda's SPI interface. @@ -100,9 +101,12 @@ def __init__(self, speed=MAX_SPEED): if spidev is None: raise PandaSpiUnavailable("spidev is not installed") - self._spidev = spidev.SpiDev() # pylint: disable=c-extension-no-member - self._spidev.open(0, 0) - self._spidev.max_speed_hz = speed + with SPI_LOCK: + if speed not in SPI_DEVICES: + SPI_DEVICES[speed] = spidev.SpiDev() # pylint: disable=c-extension-no-member + SPI_DEVICES[speed].open(0, 0) + SPI_DEVICES[speed].max_speed_hz = speed + self._spidev = SPI_DEVICES[speed] @contextmanager def acquire(self): @@ -115,8 +119,7 @@ def acquire(self): SPI_LOCK.release() def close(self): - self._spidev.close() - + pass class PandaSpiHandle(BaseHandle): From 2bd2f2a7029e629518bfec60d30891fb0e2e0a88 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Fri, 25 Oct 2024 16:11:53 -0500 Subject: [PATCH 13/33] Ford: check longitudinal actuation bits (#2067) ford long: check brake actuation bits --- board/safety/safety_ford.h | 5 +++++ tests/safety/test_ford.py | 28 ++++++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index 3ab8827b6e..c3b716c890 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -223,6 +223,9 @@ static bool ford_tx_hook(const CANPacket_t *to_send) { // Signal: CmbbDeny_B_Actl bool cmbb_deny = GET_BIT(to_send, 37U); + // Signal: AccBrkPrchg_B_Rq & AccBrkDecel_B_Rq + bool brake_actuation = GET_BIT(to_send, 54U) || GET_BIT(to_send, 55U); + bool violation = false; violation |= longitudinal_accel_checks(accel, FORD_LONG_LIMITS); violation |= longitudinal_gas_checks(gas, FORD_LONG_LIMITS); @@ -231,6 +234,8 @@ static bool ford_tx_hook(const CANPacket_t *to_send) { // Safety check for stock AEB violation |= cmbb_deny; // do not prevent stock AEB actuation + violation |= !get_longitudinal_allowed() && brake_actuation; + if (violation) { tx = false; } diff --git a/tests/safety/test_ford.py b/tests/safety/test_ford.py index 710462ae95..b854ba59ba 100755 --- a/tests/safety/test_ford.py +++ b/tests/safety/test_ford.py @@ -406,12 +406,14 @@ def setUpClass(cls): raise unittest.SkipTest # ACC command - def _acc_command_msg(self, gas: float, brake: float, cmbb_deny: bool = False): + def _acc_command_msg(self, gas: float, brake: float, brake_actuation: bool, cmbb_deny: bool = False): values = { - "AccPrpl_A_Rq": gas, # [-5|5.23] m/s^2 - "AccPrpl_A_Pred": gas, # [-5|5.23] m/s^2 - "AccBrkTot_A_Rq": brake, # [-20|11.9449] m/s^2 - "CmbbDeny_B_Actl": 1 if cmbb_deny else 0, # [0|1] deny AEB actuation + "AccPrpl_A_Rq": gas, # [-5|5.23] m/s^2 + "AccPrpl_A_Pred": gas, # [-5|5.23] m/s^2 + "AccBrkTot_A_Rq": brake, # [-20|11.9449] m/s^2 + "AccBrkPrchg_B_Rq": 1 if brake_actuation else 0, # Pre-charge brake request: 0=No, 1=Yes + "AccBrkDecel_B_Rq": 1 if brake_actuation else 0, # Deceleration request: 0=Inactive, 1=Active + "CmbbDeny_B_Actl": 1 if cmbb_deny else 0, # [0|1] deny AEB actuation } return self.packer.make_can_msg_panda("ACCDATA", 0, values) @@ -421,9 +423,9 @@ def test_stock_aeb(self): self.safety.set_controls_allowed(controls_allowed) for cmbb_deny in (True, False): should_tx = not cmbb_deny - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, self.INACTIVE_ACCEL, cmbb_deny))) + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, self.INACTIVE_ACCEL, controls_allowed, cmbb_deny))) should_tx = controls_allowed and not cmbb_deny - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.MAX_GAS, self.MAX_ACCEL, cmbb_deny))) + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.MAX_GAS, self.MAX_ACCEL, controls_allowed, cmbb_deny))) def test_gas_safety_check(self): for controls_allowed in (True, False): @@ -431,15 +433,17 @@ def test_gas_safety_check(self): for gas in np.concatenate((np.arange(self.MIN_GAS - 2, self.MAX_GAS + 2, 0.05), [self.INACTIVE_GAS])): gas = round(gas, 2) # floats might not hit exact boundary conditions without rounding should_tx = (controls_allowed and self.MIN_GAS <= gas <= self.MAX_GAS) or gas == self.INACTIVE_GAS - self.assertEqual(should_tx, self._tx(self._acc_command_msg(gas, self.INACTIVE_ACCEL))) + self.assertEqual(should_tx, self._tx(self._acc_command_msg(gas, self.INACTIVE_ACCEL, controls_allowed))) def test_brake_safety_check(self): for controls_allowed in (True, False): self.safety.set_controls_allowed(controls_allowed) - for brake in np.arange(self.MIN_ACCEL - 2, self.MAX_ACCEL + 2, 0.05): - brake = round(brake, 2) # floats might not hit exact boundary conditions without rounding - should_tx = (controls_allowed and self.MIN_ACCEL <= brake <= self.MAX_ACCEL) or brake == self.INACTIVE_ACCEL - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, brake))) + for brake_actuation in (True, False): + for brake in np.arange(self.MIN_ACCEL - 2, self.MAX_ACCEL + 2, 0.05): + brake = round(brake, 2) # floats might not hit exact boundary conditions without rounding + should_tx = (controls_allowed and self.MIN_ACCEL <= brake <= self.MAX_ACCEL) or brake == self.INACTIVE_ACCEL + should_tx = should_tx and (controls_allowed or not brake_actuation) + self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, brake, brake_actuation))) class TestFordLongitudinalSafety(TestFordLongitudinalSafetyBase): From 3066f93d8a1db92ac900e6ccc64ff68c154deafb Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Sat, 26 Oct 2024 01:27:33 -0500 Subject: [PATCH 14/33] Ford: higher curvature rate limits at low speed (#2065) * use closer to EPS limits * raise to upper bound of spread from injection plot * we've seen ~0.009 at 7 m/s, so allow that * fix test * rounding errors cause the 2 to round to the same value * adjust for latest opendbc change * we should actually match what safety is doing! fix lack of test coverage near first brake point fix * and fix test --- board/safety/safety_ford.h | 4 ++-- tests/safety/test_ford.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index c3b716c890..5b19dd9ca5 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -114,11 +114,11 @@ static const SteeringLimits FORD_STEERING_LIMITS = { .max_angle_error = 100, // 0.002 * FORD_STEERING_LIMITS.angle_deg_to_can .angle_rate_up_lookup = { {5., 25., 25.}, - {0.0002, 0.0001, 0.0001} + {0.00045, 0.0001, 0.0001} }, .angle_rate_down_lookup = { {5., 25., 25.}, - {0.000225, 0.00015, 0.00015} + {0.00045, 0.00015, 0.00015} }, // no blending at low speed due to lack of torque wind-up and inaccurate current curvature diff --git a/tests/safety/test_ford.py b/tests/safety/test_ford.py index b854ba59ba..a97e26430b 100755 --- a/tests/safety/test_ford.py +++ b/tests/safety/test_ford.py @@ -86,8 +86,8 @@ class TestFordSafetyBase(common.PandaCarSafetyTest): CURVATURE_ERROR_MIN_SPEED = 10.0 # m/s ANGLE_RATE_BP = [5., 25., 25.] - ANGLE_RATE_UP = [0.0002, 0.0001, 0.0001] # windup limit - ANGLE_RATE_DOWN = [0.000225, 0.00015, 0.00015] # unwind limit + ANGLE_RATE_UP = [0.00045, 0.0001, 0.0001] # windup limit + ANGLE_RATE_DOWN = [0.00045, 0.00015, 0.00015] # unwind limit cnt_speed = 0 cnt_speed_2 = 0 @@ -290,7 +290,7 @@ def test_curvature_rate_limit_up(self): for speed in np.arange(0, 40, 0.5): limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED - max_delta_up = np.interp(speed, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) + max_delta_up = np.interp(speed - 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) max_delta_up_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) cases = [ @@ -313,7 +313,7 @@ def test_curvature_rate_limit_down(self): for speed in np.arange(0, 40, 0.5): limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED - max_delta_down = np.interp(speed, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) + max_delta_down = np.interp(speed - 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) max_delta_down_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) cases = [ From 352e7ff138e9706715c8f0b76a985751ac620ae3 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 30 Oct 2024 16:18:01 +0100 Subject: [PATCH 15/33] cuatro beeper (#2068) beeper works Co-authored-by: Comma Device --- board/boards/cuatro.h | 14 +++++++++++--- board/drivers/beeper.h | 26 ++++++++++++++++++++++++++ board/stm32h7/board.h | 1 + board/stm32h7/peripherals.h | 1 + 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 board/drivers/beeper.h diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 00c2cf2285..545f87da8b 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -12,7 +12,7 @@ static void cuatro_set_led(uint8_t color, bool enabled) { set_gpio_output(GPIOD, 15, !enabled); break; case LED_GREEN: - set_gpio_output(GPIOD, 14, !enabled); + set_gpio_output(GPIOB, 2, !enabled); break; case LED_BLUE: set_gpio_output(GPIOE, 2, !enabled); @@ -71,12 +71,16 @@ static void cuatro_set_bootkick(BootState state) { //set_gpio_output(GPIOC, 12, state != BOOT_RESET); } +static void cuatro_set_siren(bool enabled){ + beeper_enable(enabled); +} + static void cuatro_init(void) { red_chiplet_init(); // init LEDs as open drain set_gpio_output_type(GPIOE, 2, OUTPUT_TYPE_OPEN_DRAIN); - set_gpio_output_type(GPIOD, 14, OUTPUT_TYPE_OPEN_DRAIN); + set_gpio_output_type(GPIOB, 2, OUTPUT_TYPE_OPEN_DRAIN); set_gpio_output_type(GPIOD, 15, OUTPUT_TYPE_OPEN_DRAIN); // Power readout @@ -120,6 +124,10 @@ static void cuatro_init(void) { // Clock source clock_source_init(); + + // Beeper + set_gpio_alternate(GPIOD, 14, GPIO_AF2_TIM4); + beeper_init(); } board board_cuatro = { @@ -143,7 +151,7 @@ board board_cuatro = { .read_current_mA = cuatro_read_current_mA, .set_fan_enabled = cuatro_set_fan_enabled, .set_ir_power = tres_set_ir_power, - .set_siren = unused_set_siren, + .set_siren = cuatro_set_siren, .set_bootkick = cuatro_set_bootkick, .read_som_gpio = tres_read_som_gpio }; diff --git a/board/drivers/beeper.h b/board/drivers/beeper.h new file mode 100644 index 0000000000..0ae02ff5e6 --- /dev/null +++ b/board/drivers/beeper.h @@ -0,0 +1,26 @@ + +#define BEEPER_COUNTER_OVERFLOW 25000U // 4kHz + +void beeper_enable(bool enabled) { + if (enabled) { + register_set_bits(&(TIM4->CCER), TIM_CCER_CC3E); + } else { + register_clear_bits(&(TIM4->CCER), TIM_CCER_CC3E); + } +} + +void beeper_init(void) { + // Enable timer and auto-reload + register_set(&(TIM4->CR1), TIM_CR1_CEN | TIM_CR1_ARPE, 0x3FU); + + // Set channel as PWM mode 1 and disable output + register_set_bits(&(TIM4->CCMR2), (TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE)); + beeper_enable(false); + + // Set max counter value and compare to get 50% duty + register_set(&(TIM4->CCR3), BEEPER_COUNTER_OVERFLOW / 2U, 0xFFFFU); + register_set(&(TIM4->ARR), BEEPER_COUNTER_OVERFLOW, 0xFFFFU); + + // Update registers and clear counter + TIM4->EGR |= TIM_EGR_UG; +} diff --git a/board/stm32h7/board.h b/board/stm32h7/board.h index e8ea23b252..6fbadcb633 100644 --- a/board/stm32h7/board.h +++ b/board/stm32h7/board.h @@ -10,6 +10,7 @@ #include "drivers/fan.h" #include "stm32h7/llfan.h" #include "stm32h7/lldac.h" +#include "drivers/beeper.h" #include "drivers/fake_siren.h" #include "drivers/clock_source.h" #include "boards/red.h" diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index b9a5861224..8b1cae2a68 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -126,6 +126,7 @@ void peripherals_init(void) { 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_TIM4EN; // beeper source RCC->APB1LENR |= RCC_APB1LENR_TIM6EN; // interrupt timer RCC->APB1LENR |= RCC_APB1LENR_TIM7EN; // DMA trigger timer RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // tick timer From 0b364ece1eafa2e66b71be7cade3fdfb56a3014e Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Wed, 30 Oct 2024 16:53:17 -0700 Subject: [PATCH 16/33] tres: fix I2C hang on fake siren init (#2069) * fix i2c hang * misra fix --------- Co-authored-by: Comma Device --- board/boards/tres.h | 1 - board/drivers/fake_siren.h | 8 ++++++++ board/main.c | 5 +++-- board/stm32h7/lli2c.h | 32 +++++++++++++++++++++++--------- python/__init__.py | 1 - 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/board/boards/tres.h b/board/boards/tres.h index 2ec0650dec..1e5c8fe0ea 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -69,7 +69,6 @@ static void tres_init(void) { set_gpio_alternate(GPIOC, 10, GPIO_AF4_I2C5); set_gpio_alternate(GPIOC, 11, GPIO_AF4_I2C5); register_set_bits(&(GPIOC->OTYPER), GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11); // open drain - fake_siren_init(); // Clock source clock_source_init(); diff --git a/board/drivers/fake_siren.h b/board/drivers/fake_siren.h index feedd8fe90..9468afd806 100644 --- a/board/drivers/fake_siren.h +++ b/board/drivers/fake_siren.h @@ -2,6 +2,8 @@ #define CODEC_I2C_ADDR 0x10 +void fake_siren_init(void); + void fake_siren_codec_enable(bool enabled) { if (enabled) { bool success = true; @@ -28,8 +30,14 @@ void fake_siren_codec_enable(bool enabled) { void fake_siren_set(bool enabled) { + static bool initialized = false; static bool fake_siren_enabled = false; + if (!initialized) { + fake_siren_init(); + initialized = true; + } + if (enabled != fake_siren_enabled) { fake_siren_codec_enable(enabled); } diff --git a/board/main.c b/board/main.c index 9264e06bbe..0c2e9575fb 100644 --- a/board/main.c +++ b/board/main.c @@ -325,12 +325,13 @@ int main(void) { // panda has an FPU, let's use it! enable_fpu(); + microsecond_timer_init(); + + current_board->set_siren(false); if (current_board->fan_max_rpm > 0U) { fan_init(); } - microsecond_timer_init(); - // init to SILENT and can silent set_safety_mode(SAFETY_SILENT, 0U); diff --git a/board/stm32h7/lli2c.h b/board/stm32h7/lli2c.h index b423c94bdd..5d79beb653 100644 --- a/board/stm32h7/lli2c.h +++ b/board/stm32h7/lli2c.h @@ -1,20 +1,27 @@ - // TODO: this driver relies heavily on polling, // if we want it to be more async, we should use interrupts +#define I2C_RETRY_COUNT 10U #define I2C_TIMEOUT_US 100000U -// cppcheck-suppress misra-c2012-2.7; not sure why it triggers here? bool i2c_status_wait(const volatile uint32_t *reg, uint32_t mask, uint32_t val) { uint32_t start_time = microsecond_timer_get(); while(((*reg & mask) != val) && (get_ts_elapsed(microsecond_timer_get(), start_time) < I2C_TIMEOUT_US)); return ((*reg & mask) == val); } +void i2c_reset(I2C_TypeDef *I2C) { + // peripheral reset + register_clear_bits(&I2C->CR1, I2C_CR1_PE); + while ((I2C->CR1 & I2C_CR1_PE) != 0U); + register_set_bits(&I2C->CR1, I2C_CR1_PE); +} + bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value) { - // Setup transfer and send START + addr bool ret = false; - for(uint32_t i=0U; i<10U; i++) { + + // Setup transfer and send START + addr + for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); @@ -57,9 +64,10 @@ bool i2c_write_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t value) { } bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value) { - // Setup transfer and send START + addr bool ret = false; - for(uint32_t i=0U; i<10U; i++) { + + // Setup transfer and send START + addr + for (uint32_t i = 0U; i < I2C_RETRY_COUNT; i++) { register_clear_bits(&I2C->CR2, I2C_CR2_ADD10); I2C->CR2 = ((uint32_t)addr << 1U) & I2C_CR2_SADD_Msk; register_clear_bits(&I2C->CR2, I2C_CR2_RD_WRN); @@ -116,6 +124,11 @@ bool i2c_read_reg(I2C_TypeDef *I2C, uint8_t addr, uint8_t reg, uint8_t *value) { I2C->CR2 |= I2C_CR2_STOP; end: + + if (!ret) { + i2c_reset(I2C); + } + return ret; } @@ -131,7 +144,7 @@ bool i2c_set_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bool i2c_clear_reg_bits(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t bits) { uint8_t value; bool ret = i2c_read_reg(I2C, address, regis, &value); - if(ret) { + if (ret) { ret = i2c_write_reg(I2C, address, regis, value & (uint8_t) (~bits)); } return ret; @@ -149,5 +162,6 @@ bool i2c_set_reg_mask(I2C_TypeDef *I2C, uint8_t address, uint8_t regis, uint8_t void i2c_init(I2C_TypeDef *I2C) { // 100kHz clock speed I2C->TIMINGR = 0x107075B0; - I2C->CR1 = I2C_CR1_PE; -} \ No newline at end of file + + i2c_reset(I2C); +} diff --git a/python/__init__.py b/python/__init__.py index 0cbf1ef94b..14d324ab93 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -252,7 +252,6 @@ def _cli_select_panda(self): return None if len(pandas) == 1: print(f"INFO: connecting to panda {pandas[0]}") - time.sleep(1) return pandas[0] while True: print("Multiple pandas available:") From 991c8447313c5c88246268fe3f581a9209eb6eb5 Mon Sep 17 00:00:00 2001 From: Greg Hogan Date: Mon, 4 Nov 2024 16:16:25 -0800 Subject: [PATCH 17/33] SocketPanda python library (#2073) * SocketPanda python library * fix spelling * can0 should be the default * CAN-FD support --- python/socketpanda.py | 94 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 python/socketpanda.py diff --git a/python/socketpanda.py b/python/socketpanda.py new file mode 100644 index 0000000000..d6115acf55 --- /dev/null +++ b/python/socketpanda.py @@ -0,0 +1,94 @@ +import socket +import struct + +# /** +# * struct canfd_frame - CAN flexible data rate frame structure +# * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition +# * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN) +# * @flags: additional flags for CAN FD +# * @__res0: reserved / padding +# * @__res1: reserved / padding +# * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte) +# */ +# struct canfd_frame { +# canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ +# __u8 len; /* frame payload length in byte */ +# __u8 flags; /* additional flags for CAN FD */ +# __u8 __res0; /* reserved / padding */ +# __u8 __res1; /* reserved / padding */ +# __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8))); +# }; +CAN_HEADER_FMT = "=IBB2x" +CAN_HEADER_LEN = struct.calcsize(CAN_HEADER_FMT) +CAN_MAX_DLEN = 8 +CANFD_MAX_DLEN = 64 + +CANFD_BRS = 0x01 # bit rate switch (second bitrate for payload data) +CANFD_FDF = 0x04 # mark CAN FD for dual use of struct canfd_frame + +# socket.SO_RXQ_OVFL is missing +# https://github.com/torvalds/linux/blob/47ac09b91befbb6a235ab620c32af719f8208399/include/uapi/asm-generic/socket.h#L61 +SO_RXQ_OVFL = 40 + +def create_socketcan(interface:str, recv_buffer_size:int, fd:bool) -> socket.socket: + # settings mostly from https://github.com/linux-can/can-utils/blob/master/candump.c + socketcan = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW) + if fd: + socketcan.setsockopt(socket.SOL_CAN_RAW, socket.CAN_RAW_FD_FRAMES, 1) + socketcan.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, recv_buffer_size) + # TODO: why is it always 2x the requested size? + assert socketcan.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) == recv_buffer_size * 2 + # TODO: how to dectect and alert on buffer overflow? + socketcan.setsockopt(socket.SOL_SOCKET, SO_RXQ_OVFL, 1) + socketcan.bind((interface,)) + return socketcan + +# Panda class substitute for socketcan device (to support using the uds/iso-tp/xcp/ccp library) +class SocketPanda(): + def __init__(self, interface:str="can0", bus:int=0, fd:bool=False, recv_buffer_size:int=212992) -> None: + self.interface = interface + self.bus = bus + self.fd = fd + self.flags = CANFD_BRS | CANFD_FDF if fd else 0 + self.data_len = CANFD_MAX_DLEN if fd else CAN_MAX_DLEN + self.recv_buffer_size = recv_buffer_size + self.socket = create_socketcan(interface, recv_buffer_size, fd) + + def __del__(self): + self.socket.close() + + def get_serial(self) -> tuple[int, int]: + return (0, 0) # TODO: implemented in panda socketcan driver + + def get_version(self) -> int: + return 0 # TODO: implemented in panda socketcan driver + + def can_clear(self, bus:int) -> None: + # TODO: implemented in panda socketcan driver + self.socket.close() + self.socket = create_socketcan(self.interface, self.recv_buffer_size, self.fd) + + def set_safety_mode(self, mode:int, param=0) -> None: + pass # TODO: implemented in panda socketcan driver + + def has_obd(self) -> bool: + return False # TODO: implemented in panda socketcan driver + + def can_send(self, addr, dat, bus=0, timeout=0) -> None: + msg_len = len(dat) + msg_dat = dat.ljust(self.data_len, b'\x00') + can_frame = struct.pack(CAN_HEADER_FMT, addr, msg_len, self.flags) + msg_dat + self.socket.sendto(can_frame, (self.interface,)) + + def can_recv(self) -> list[tuple[int, bytes, int]]: + msgs = list() + while True: + try: + dat, _ = self.socket.recvfrom(self.recv_buffer_size, socket.MSG_DONTWAIT) + assert len(dat) == CAN_HEADER_LEN + self.data_len, f"ERROR: received {len(dat)} bytes" + can_id, msg_len, _ = struct.unpack(CAN_HEADER_FMT, dat[:CAN_HEADER_LEN]) + msg_dat = dat[CAN_HEADER_LEN:CAN_HEADER_LEN+msg_len] + msgs.append((can_id, msg_dat, self.bus)) + except BlockingIOError: + break # buffered data exhausted + return msgs From aab03bc4b6ab02be7db3fd60f034a84d79ad93b4 Mon Sep 17 00:00:00 2001 From: Radek Date: Wed, 6 Nov 2024 16:09:42 +0000 Subject: [PATCH 18/33] Make can_print.py work on a all busses (#2070) * Enable CAN3 printer (#1991) again (cherry picked from commit 2b94e4fef710a22313b99d8c5b723d8db5e9c8bd) * print messages with same address but on different bus --- board/jungle/scripts/can_printer.py | 18 +++++++++++------- tests/can_printer.py | 18 +++++++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/board/jungle/scripts/can_printer.py b/board/jungle/scripts/can_printer.py index 05cc802aa6..292421a5c6 100755 --- a/board/jungle/scripts/can_printer.py +++ b/board/jungle/scripts/can_printer.py @@ -19,20 +19,24 @@ def can_printer(): start = sec_since_boot() lp = sec_since_boot() - msgs = defaultdict(list) + all_msgs = defaultdict(list) + canbus = os.getenv("CAN") + if canbus == "3": + p.set_obd(True) + canbus = "1" + while True: can_recv = p.can_recv() - for address,dat, src in can_recv: - if canbus is None or str(src) == canbus: - msgs[address].append((dat, src)) + for addr, dat, bus in can_recv: + if canbus is None or str(bus) == canbus: + all_msgs[(addr, bus)].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(msgs.items()): - last_msg, last_src = v[-1] - dd += "%d: %s(%6d): %s\n" % (last_src, "%04X(%4d)" % (k, k), len(v), binascii.hexlify(last_msg).decode()) + for (addr, bus), dat_log in sorted(all_msgs.items()): + dd += "%d: %s(%6d): %s\n" % (bus, "%04X(%4d)" % (addr, addr), len(dat_log), binascii.hexlify(dat_log[-1]).decode()) print(dd) lp = sec_since_boot() diff --git a/tests/can_printer.py b/tests/can_printer.py index 791f8e75dc..404bc9dcf7 100755 --- a/tests/can_printer.py +++ b/tests/can_printer.py @@ -20,20 +20,24 @@ def can_printer(): start = sec_since_boot() lp = sec_since_boot() - msgs = defaultdict(list) + all_msgs = defaultdict(list) + canbus = os.getenv("CAN") + if canbus == "3": + p.set_obd(True) + canbus = "1" + while True: can_recv = p.can_recv() - for address, dat, src in can_recv: - if canbus is None or str(src) == canbus: - msgs[address].append((dat, src)) + for addr, dat, bus in can_recv: + if canbus is None or str(bus) == canbus: + all_msgs[(addr, bus)].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(msgs.items()): - last_msg, last_src = v[-1] - dd += "%d: %s(%6d): %s\n" % (last_src, "%04X(%4d)" % (k, k), len(v), binascii.hexlify(last_msg).decode()) + for (addr, bus), dat_log in sorted(all_msgs.items()): + dd += "%d: %s(%6d): %s\n" % (bus, "%04X(%4d)" % (addr, addr), len(dat_log), binascii.hexlify(dat_log[-1]).decode()) print(dd) lp = sec_since_boot() From ae4f753582c1034f09e690a70912030c858644f2 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 7 Nov 2024 16:55:30 -0800 Subject: [PATCH 19/33] spi: add helper for getting bootloader ID (#2074) * spi: add helper for getting bootloader ID * fix mypy --------- Co-authored-by: Comma Device --- python/spi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/spi.py b/python/spi.py index a6832beaed..5be1e56b6b 100644 --- a/python/spi.py +++ b/python/spi.py @@ -397,6 +397,9 @@ def read(self, address: int, length: int): data = [struct.pack('>I', address), struct.pack('B', length - 1)] return self._cmd(0x11, data=data, read_bytes=length) + def get_bootloader_id(self): + return self.read(0x1FF1E7FE, 1) + def get_chip_id(self) -> int: r = self._cmd(0x02, read_bytes=3) assert r[0] == 1 # response length - 1 From dec9223f9726e400e4a4eb91ca19fffcd745f97a Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Wed, 13 Nov 2024 16:09:13 +0100 Subject: [PATCH 20/33] cuatro sound (#1861) * fake siren works * this receives something * microphone in SAI1 POC * this receives audio again * double buffer DMA? * RX DMA works * wip * needs cleanup, but this plays * cleanup of playback * print mic * deal with stereo in * the DMA from SAI1 -> SAI4 doesn't work yet * this puts mic data in a buf * this gets sound to the 845 * wip: still garbage from the mic * inefficient, but it does record the mic * sine isn't used * comment out mic for now * fix misra * remove mic * more cleanup * add amp enable/disable * no more debug * newline --------- Co-authored-by: Comma Device --- board/boards/black.h | 3 +- board/boards/board_declarations.h | 2 + board/boards/cuatro.h | 18 +++++- board/boards/dos.h | 3 +- board/boards/grey.h | 3 +- board/boards/red.h | 3 +- board/boards/tres.h | 3 +- board/boards/uno.h | 3 +- board/boards/unused_funcs.h | 4 ++ board/boards/white.h | 3 +- board/faults_declarations.h | 1 + board/main.c | 1 + board/stm32f4/board.h | 3 + board/stm32h7/board.h | 1 + board/stm32h7/peripherals.h | 6 +- board/stm32h7/sound.h | 95 +++++++++++++++++++++++++++++++ 16 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 board/stm32h7/sound.h diff --git a/board/boards/black.h b/board/boards/black.h index 45e0126cc4..133c11fb2d 100644 --- a/board/boards/black.h +++ b/board/boards/black.h @@ -182,5 +182,6 @@ board board_black = { .set_fan_enabled = unused_set_fan_enabled, .set_ir_power = unused_set_ir_power, .set_siren = unused_set_siren, - .read_som_gpio = unused_read_som_gpio + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/board_declarations.h b/board/boards/board_declarations.h index dbb29ffb79..61e9ce2d8e 100644 --- a/board/boards/board_declarations.h +++ b/board/boards/board_declarations.h @@ -24,6 +24,7 @@ typedef void (*board_set_fan_enabled)(bool enabled); typedef void (*board_set_siren)(bool enabled); typedef void (*board_set_bootkick)(BootState state); typedef bool (*board_read_som_gpio)(void); +typedef void (*board_set_amp_enabled)(bool enabled); struct board { harness_configuration *harness_config; @@ -49,6 +50,7 @@ struct board { board_set_siren set_siren; board_set_bootkick set_bootkick; board_read_som_gpio read_som_gpio; + board_set_amp_enabled set_amp_enabled; }; // ******************* Definitions ******************** diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 545f87da8b..4a6b5da096 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -75,6 +75,10 @@ static void cuatro_set_siren(bool enabled){ beeper_enable(enabled); } +static void cuatro_set_amp_enabled(bool enabled){ + set_gpio_output(GPIOA, 5, enabled); +} + static void cuatro_init(void) { red_chiplet_init(); @@ -128,6 +132,17 @@ static void cuatro_init(void) { // Beeper set_gpio_alternate(GPIOD, 14, GPIO_AF2_TIM4); beeper_init(); + + // Sound codec + cuatro_set_amp_enabled(false); + set_gpio_alternate(GPIOA, 2, GPIO_AF8_SAI4); // SAI4_SCK_B + set_gpio_alternate(GPIOC, 0, GPIO_AF8_SAI4); // SAI4_FS_B + set_gpio_alternate(GPIOD, 11, GPIO_AF10_SAI4); // SAI4_SD_A + set_gpio_alternate(GPIOE, 3, GPIO_AF8_SAI4); // SAI4_SD_B + set_gpio_alternate(GPIOE, 4, GPIO_AF2_SAI1); // SAI1_D2 + set_gpio_alternate(GPIOE, 5, GPIO_AF2_SAI1); // SAI1_CK2 + set_gpio_alternate(GPIOE, 6, GPIO_AF10_SAI4); // SAI4_MCLK_B + sound_init(); } board board_cuatro = { @@ -153,5 +168,6 @@ board board_cuatro = { .set_ir_power = tres_set_ir_power, .set_siren = cuatro_set_siren, .set_bootkick = cuatro_set_bootkick, - .read_som_gpio = tres_read_som_gpio + .read_som_gpio = tres_read_som_gpio, + .set_amp_enabled = cuatro_set_amp_enabled }; diff --git a/board/boards/dos.h b/board/boards/dos.h index 2c2fd5d4d6..d96cee4ca4 100644 --- a/board/boards/dos.h +++ b/board/boards/dos.h @@ -211,5 +211,6 @@ board board_dos = { .set_ir_power = dos_set_ir_power, .set_siren = dos_set_siren, .set_bootkick = dos_set_bootkick, - .read_som_gpio = dos_read_som_gpio + .read_som_gpio = dos_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/grey.h b/board/boards/grey.h index c5c5321f6e..b3e40bc6fe 100644 --- a/board/boards/grey.h +++ b/board/boards/grey.h @@ -31,5 +31,6 @@ board board_grey = { .set_fan_enabled = unused_set_fan_enabled, .set_ir_power = unused_set_ir_power, .set_siren = unused_set_siren, - .read_som_gpio = unused_read_som_gpio + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/red.h b/board/boards/red.h index ac062772f0..f1d8d9c44b 100644 --- a/board/boards/red.h +++ b/board/boards/red.h @@ -194,5 +194,6 @@ board board_red = { .set_fan_enabled = unused_set_fan_enabled, .set_ir_power = unused_set_ir_power, .set_siren = unused_set_siren, - .read_som_gpio = unused_read_som_gpio + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/tres.h b/board/boards/tres.h index 1e5c8fe0ea..f0602ae60b 100644 --- a/board/boards/tres.h +++ b/board/boards/tres.h @@ -97,5 +97,6 @@ board board_tres = { .set_ir_power = tres_set_ir_power, .set_siren = fake_siren_set, .set_bootkick = tres_set_bootkick, - .read_som_gpio = tres_read_som_gpio + .read_som_gpio = tres_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/uno.h b/board/boards/uno.h index 2e74c249dc..d1728f1f9f 100644 --- a/board/boards/uno.h +++ b/board/boards/uno.h @@ -218,5 +218,6 @@ board board_uno = { .set_ir_power = uno_set_ir_power, .set_siren = unused_set_siren, .set_bootkick = uno_set_bootkick, - .read_som_gpio = unused_read_som_gpio + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/boards/unused_funcs.h b/board/boards/unused_funcs.h index e9c18c76a3..588cf63654 100644 --- a/board/boards/unused_funcs.h +++ b/board/boards/unused_funcs.h @@ -26,3 +26,7 @@ void unused_set_bootkick(BootState state) { bool unused_read_som_gpio(void) { return false; } + +void unused_set_amp_enabled(bool enabled) { + UNUSED(enabled); +} diff --git a/board/boards/white.h b/board/boards/white.h index 2ef8de27a4..c4a2559353 100644 --- a/board/boards/white.h +++ b/board/boards/white.h @@ -211,5 +211,6 @@ board board_white = { .set_fan_enabled = unused_set_fan_enabled, .set_ir_power = unused_set_ir_power, .set_siren = unused_set_siren, - .read_som_gpio = unused_read_som_gpio + .read_som_gpio = unused_read_som_gpio, + .set_amp_enabled = unused_set_amp_enabled }; diff --git a/board/faults_declarations.h b/board/faults_declarations.h index 4ef03087ba..eab9fcc08b 100644 --- a/board/faults_declarations.h +++ b/board/faults_declarations.h @@ -32,6 +32,7 @@ #define FAULT_INTERRUPT_RATE_UART_7 (1UL << 24) #define FAULT_SIREN_MALFUNCTION (1UL << 25) #define FAULT_HEARTBEAT_LOOP_WATCHDOG (1UL << 26) +#define FAULT_INTERRUPT_RATE_SOUND_DMA (1UL << 27) // Permanent faults #define PERMANENT_FAULTS 0U diff --git a/board/main.c b/board/main.c index 0c2e9575fb..b1dc685417 100644 --- a/board/main.c +++ b/board/main.c @@ -158,6 +158,7 @@ static void tick_handler(void) { usb_tick(); harness_tick(); simple_watchdog_kick(); + sound_tick(); // re-init everything that uses harness status if (harness.status != prev_harness_status) { diff --git a/board/stm32f4/board.h b/board/stm32f4/board.h index bf95d58eaa..0cbaae6025 100644 --- a/board/stm32f4/board.h +++ b/board/stm32f4/board.h @@ -16,6 +16,9 @@ #include "boards/uno.h" #include "boards/dos.h" +// Unused functions on F4 +void sound_tick(void) {} + void detect_board_type(void) { // SPI lines floating: white (TODO: is this reliable? Not really, we have to enable ESP/GPS to be able to detect this on the UART) set_gpio_output(GPIOC, 14, 1); diff --git a/board/stm32h7/board.h b/board/stm32h7/board.h index 6fbadcb633..ea4812245e 100644 --- a/board/stm32h7/board.h +++ b/board/stm32h7/board.h @@ -12,6 +12,7 @@ #include "stm32h7/lldac.h" #include "drivers/beeper.h" #include "drivers/fake_siren.h" +#include "stm32h7/sound.h" #include "drivers/clock_source.h" #include "boards/red.h" #include "boards/red_chiplet.h" diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index 8b1cae2a68..f0bd727fdf 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -101,13 +101,14 @@ void peripherals_init(void) { RCC->AHB4ENR |= RCC_AHB4ENR_GPIOFEN; RCC->AHB4ENR |= RCC_AHB4ENR_GPIOGEN; - // Enable CPU access to SRAM1 and SRAM2 (in domain D2) for DMA + // Enable CPU access to SRAMs 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; + RCC->AHB4ENR |= RCC_AHB4ENR_BDMAEN; // Audio DMA // Connectivity RCC->APB2ENR |= RCC_APB2ENR_SPI4EN; // SPI @@ -122,6 +123,9 @@ void peripherals_init(void) { RCC->AHB1ENR |= RCC_AHB1ENR_ADC12EN; // Enable ADC12 clocks RCC->APB1LENR |= RCC_APB1LENR_DAC12EN; // DAC + // Audio + RCC->APB4ENR |= RCC_APB4ENR_SAI4EN; // SAI4 + // Timers RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer RCC->APB1LENR |= RCC_APB1LENR_TIM2EN; // main counter diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h new file mode 100644 index 0000000000..9228b16134 --- /dev/null +++ b/board/stm32h7/sound.h @@ -0,0 +1,95 @@ + +#define SOUND_RX_BUF_SIZE 2000U +#define SOUND_TX_BUF_SIZE (SOUND_RX_BUF_SIZE/2U) + +__attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_SIZE]; + +typedef enum { + OFF = 0, + IDLE = 1, + PLAYING = 2, +} SoundStatus; +static SoundStatus sound_status = OFF; + +// Playback processing +static void BDMA_Channel0_IRQ_Handler(void) { + __attribute__((section(".sram4"))) static uint16_t tx_buf[SOUND_TX_BUF_SIZE]; + + BDMA->IFCR |= BDMA_IFCR_CGIF0; // clear flag + + // process samples (shift to 12b and bias to be unsigned) + // since we are playing mono and receiving stereo, we take every other sample + uint8_t buf_idx = (((BDMA_Channel0->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; + for (uint16_t i=0U; i < SOUND_RX_BUF_SIZE; i += 2U) { + tx_buf[i/2U] = ((sound_rx_buf[buf_idx][i] + (1UL << 14)) >> 3); + } + + if (sound_status == OFF) { + current_board->set_amp_enabled(true); + } + sound_status = PLAYING; + + DMA1->LIFCR |= 0xF40; + DMA1_Stream1->CR &= ~DMA_SxCR_EN; + register_set(&DMA1_Stream1->M0AR, (uint32_t) tx_buf, 0xFFFFFFFFU); + DMA1_Stream1->NDTR = SOUND_TX_BUF_SIZE; + DMA1_Stream1->CR |= DMA_SxCR_EN; +} + +void sound_tick(void) { + switch (sound_status) { + case IDLE: + current_board->set_amp_enabled(false); + sound_status = OFF; + break; + case PLAYING: + sound_status = IDLE; + break; + default: + break; + } +} + +void sound_init(void) { + REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 64U, FAULT_INTERRUPT_RATE_SOUND_DMA) + + // Init DAC + register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set_bits(&DAC1->CR, DAC_CR_EN1); + + // Setup DMAMUX (DAC_CH1_DMA as input) + register_set(&DMAMUX1_Channel1->CCR, 67U, DMAMUX_CxCR_DMAREQ_ID_Msk); + + // Setup DMA + register_set(&DMA1_Stream1->PAR, (uint32_t) &(DAC1->DHR12R1), 0xFFFFFFFFU); + register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); + DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | (0b01UL << DMA_SxCR_MSIZE_Pos) | (0b01UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | (1U << DMA_SxCR_DIR_Pos); + + // Init trigger timer (48kHz) + register_set(&TIM7->PSC, 0U, 0xFFFFU); + register_set(&TIM7->ARR, 2494U, 0xFFFFU); + register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); + TIM7->SR = 0U; + TIM7->CR1 |= TIM_CR1_CEN; + + // stereo audio in + register_set(&SAI4_Block_B->CR1, SAI_xCR1_DMAEN | (0b00UL << SAI_xCR1_SYNCEN_Pos) | (0b100U << SAI_xCR1_DS_Pos) | (0b11U << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); + register_set(&SAI4_Block_B->CR2, (0b001U << SAI_xCR2_FTH_Pos), 0xFFFBU); + register_set(&SAI4_Block_B->FRCR, (31U << SAI_xFRCR_FRL_Pos), 0x7FFFFU); + register_set(&SAI4_Block_B->SLOTR, (0b11UL << SAI_xSLOTR_SLOTEN_Pos) | (1UL << SAI_xSLOTR_NBSLOT_Pos) | (0b01UL << SAI_xSLOTR_SLOTSZ_Pos), 0xFFFF0FDFU); // NBSLOT definition is vague + + // init sound DMA (SAI4_B -> memory, double buffers) + register_set(&BDMA_Channel0->CPAR, (uint32_t) &(SAI4_Block_B->DR), 0xFFFFFFFFU); + register_set(&BDMA_Channel0->CM0AR, (uint32_t) sound_rx_buf[0], 0xFFFFFFFFU); + register_set(&BDMA_Channel0->CM1AR, (uint32_t) sound_rx_buf[1], 0xFFFFFFFFU); + BDMA_Channel0->CNDTR = SOUND_RX_BUF_SIZE; + register_set(&BDMA_Channel0->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) | (0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | BDMA_CCR_TCIE, 0xFFFFU); + register_set(&DMAMUX2_Channel0->CCR, 16U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_B_DMA + register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN); + + // enable all initted blocks + register_set_bits(&SAI4_Block_B->CR1, SAI_xCR1_SAIEN); + NVIC_EnableIRQ(BDMA_Channel0_IRQn); +} From 39c966fb271ebe3cd589272c0fb3a45eb0c54280 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 14 Nov 2024 01:04:59 -0800 Subject: [PATCH 21/33] sound: little cleaner idle handling (#2078) Co-authored-by: Comma Device --- board/stm32h7/sound.h | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h index 9228b16134..53f7276bde 100644 --- a/board/stm32h7/sound.h +++ b/board/stm32h7/sound.h @@ -1,15 +1,9 @@ - #define SOUND_RX_BUF_SIZE 2000U #define SOUND_TX_BUF_SIZE (SOUND_RX_BUF_SIZE/2U) __attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_SIZE]; -typedef enum { - OFF = 0, - IDLE = 1, - PLAYING = 2, -} SoundStatus; -static SoundStatus sound_status = OFF; +static uint8_t sound_idle_count; // Playback processing static void BDMA_Channel0_IRQ_Handler(void) { @@ -24,10 +18,10 @@ static void BDMA_Channel0_IRQ_Handler(void) { tx_buf[i/2U] = ((sound_rx_buf[buf_idx][i] + (1UL << 14)) >> 3); } - if (sound_status == OFF) { + if (sound_idle_count == 0U) { current_board->set_amp_enabled(true); } - sound_status = PLAYING; + sound_idle_count = 2U; DMA1->LIFCR |= 0xF40; DMA1_Stream1->CR &= ~DMA_SxCR_EN; @@ -37,16 +31,11 @@ static void BDMA_Channel0_IRQ_Handler(void) { } void sound_tick(void) { - switch (sound_status) { - case IDLE: + if (sound_idle_count > 0U) { + sound_idle_count--; + if (sound_idle_count == 0U) { current_board->set_amp_enabled(false); - sound_status = OFF; - break; - case PLAYING: - sound_status = IDLE; - break; - default: - break; + } } } From a67f3650c8f28ae2eebfbc561f0e68ed84b87d46 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Thu, 14 Nov 2024 11:28:28 +0100 Subject: [PATCH 22/33] Increase MISRA to 2min max (#2079) increase to 2min max --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 221bce0096..367cf2dc36 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -99,7 +99,7 @@ jobs: - name: Build FW run: ${{ env.RUN }} "scons -j$(nproc)" - name: Run MISRA C:2012 analysis - timeout-minutes: 1 + timeout-minutes: 2 run: ${{ env.RUN }} "cd tests/misra && ./test_misra.sh" - name: MISRA mutation tests timeout-minutes: 5 From 742d961b07e602bb143086632d0f8150f4882f5f Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 14 Nov 2024 17:05:44 -0800 Subject: [PATCH 23/33] Lower siren duration to 3s (#2080) --- board/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/main.c b/board/main.c index b1dc685417..aaea95d089 100644 --- a/board/main.c +++ b/board/main.c @@ -241,7 +241,7 @@ static void tick_handler(void) { print(" seconds. Safety is set to SILENT mode.\n"); if (controls_allowed_countdown > 0U) { - siren_countdown = 5U; + siren_countdown = 3U; controls_allowed_countdown = 0U; } From acc15afaa0bea090c045a624f940e98cc521b701 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Fri, 15 Nov 2024 15:35:30 +0100 Subject: [PATCH 24/33] Fix sound playback artifacts (#2082) this stays in sync Co-authored-by: Comma Device --- board/stm32h7/peripherals.h | 1 + board/stm32h7/sound.h | 17 +++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/board/stm32h7/peripherals.h b/board/stm32h7/peripherals.h index f0bd727fdf..c6874b8ac3 100644 --- a/board/stm32h7/peripherals.h +++ b/board/stm32h7/peripherals.h @@ -135,6 +135,7 @@ void peripherals_init(void) { RCC->APB1LENR |= RCC_APB1LENR_TIM7EN; // DMA trigger timer RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // tick timer RCC->APB1LENR |= RCC_APB1LENR_TIM12EN; // slow loop + RCC->APB1LENR |= RCC_APB1LENR_TIM5EN; // sound trigger timer #ifdef PANDA_JUNGLE RCC->AHB3ENR |= RCC_AHB3ENR_SDMMC1EN; // SDMMC diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h index 53f7276bde..f40a22e5b2 100644 --- a/board/stm32h7/sound.h +++ b/board/stm32h7/sound.h @@ -44,7 +44,7 @@ void sound_init(void) { // Init DAC register_set(&DAC1->MCR, 0U, 0xFFFFFFFFU); - register_set(&DAC1->CR, DAC_CR_TEN1 | (6U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); + register_set(&DAC1->CR, DAC_CR_TEN1 | (4U << DAC_CR_TSEL1_Pos) | DAC_CR_DMAEN1, 0xFFFFFFFFU); register_set_bits(&DAC1->CR, DAC_CR_EN1); // Setup DMAMUX (DAC_CH1_DMA as input) @@ -55,13 +55,14 @@ void sound_init(void) { register_set(&DMA1_Stream1->FCR, 0U, 0x00000083U); DMA1_Stream1->CR = (0b11UL << DMA_SxCR_PL_Pos) | (0b01UL << DMA_SxCR_MSIZE_Pos) | (0b01UL << DMA_SxCR_PSIZE_Pos) | DMA_SxCR_MINC | (1U << DMA_SxCR_DIR_Pos); - // Init trigger timer (48kHz) - register_set(&TIM7->PSC, 0U, 0xFFFFU); - register_set(&TIM7->ARR, 2494U, 0xFFFFU); - register_set(&TIM7->CR2, (0b10U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); - register_set(&TIM7->CR1, TIM_CR1_ARPE | TIM_CR1_URS, 0x088EU); - TIM7->SR = 0U; - TIM7->CR1 |= TIM_CR1_CEN; + // Init trigger timer (little slower than 48kHz, pulled in sync by SAI4_FS_B) + register_set(&TIM5->PSC, 2600U, 0xFFFFU); + register_set(&TIM5->ARR, 100U, 0xFFFFFFFFU); // not important + register_set(&TIM5->AF1, (0b0010UL << TIM5_AF1_ETRSEL_Pos), TIM5_AF1_ETRSEL_Msk); + register_set(&TIM5->CR2, (0b010U << TIM_CR2_MMS_Pos), TIM_CR2_MMS_Msk); + register_set(&TIM5->SMCR, TIM_SMCR_ECE | (0b00111UL << TIM_SMCR_TS_Pos)| (0b0100UL << TIM_SMCR_SMS_Pos), 0x31FFF7U); + TIM5->CNT = 0U; TIM5->SR = 0U; + TIM5->CR1 |= TIM_CR1_CEN; // stereo audio in register_set(&SAI4_Block_B->CR1, SAI_xCR1_DMAEN | (0b00UL << SAI_xCR1_SYNCEN_Pos) | (0b100U << SAI_xCR1_DS_Pos) | (0b11U << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU); From 422e3e0c0f458c721517454cefcd22407576e9d5 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Mon, 18 Nov 2024 14:50:17 -0800 Subject: [PATCH 25/33] H7 sound: fix noise with empty stream (#2083) fix noise Co-authored-by: Comma Device --- board/stm32h7/sound.h | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/board/stm32h7/sound.h b/board/stm32h7/sound.h index f40a22e5b2..6251936c62 100644 --- a/board/stm32h7/sound.h +++ b/board/stm32h7/sound.h @@ -5,23 +5,41 @@ __attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_ static uint8_t sound_idle_count; +void sound_tick(void) { + if (sound_idle_count > 0U) { + sound_idle_count--; + if (sound_idle_count == 0U) { + current_board->set_amp_enabled(false); + } + } +} + // Playback processing static void BDMA_Channel0_IRQ_Handler(void) { __attribute__((section(".sram4"))) static uint16_t tx_buf[SOUND_TX_BUF_SIZE]; BDMA->IFCR |= BDMA_IFCR_CGIF0; // clear flag - // process samples (shift to 12b and bias to be unsigned) - // since we are playing mono and receiving stereo, we take every other sample uint8_t buf_idx = (((BDMA_Channel0->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U; + + // process samples (shift to 12b and bias to be unsigned) + bool sound_playing = false; for (uint16_t i=0U; i < SOUND_RX_BUF_SIZE; i += 2U) { + // since we are playing mono and receiving stereo, we take every other sample tx_buf[i/2U] = ((sound_rx_buf[buf_idx][i] + (1UL << 14)) >> 3); + if (sound_rx_buf[buf_idx][i] > 0U) { + sound_playing = true; + } } - if (sound_idle_count == 0U) { - current_board->set_amp_enabled(true); + // manage amp state + if (sound_playing) { + if (sound_idle_count == 0U) { + current_board->set_amp_enabled(true); + } + sound_idle_count = 4U; } - sound_idle_count = 2U; + sound_tick(); DMA1->LIFCR |= 0xF40; DMA1_Stream1->CR &= ~DMA_SxCR_EN; @@ -30,15 +48,6 @@ static void BDMA_Channel0_IRQ_Handler(void) { DMA1_Stream1->CR |= DMA_SxCR_EN; } -void sound_tick(void) { - if (sound_idle_count > 0U) { - sound_idle_count--; - if (sound_idle_count == 0U) { - current_board->set_amp_enabled(false); - } - } -} - void sound_init(void) { REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 64U, FAULT_INTERRUPT_RATE_SOUND_DMA) From 998a63936087a3d90cd29df88375d786491a78f8 Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Wed, 20 Nov 2024 19:22:14 -0800 Subject: [PATCH 26/33] python: catch exceptions in DFU (#2084) * try * also this --- python/dfu.py | 13 ++++++++----- python/spi.py | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/python/dfu.py b/python/dfu.py index 9beba45e55..4394d2502d 100644 --- a/python/dfu.py +++ b/python/dfu.py @@ -100,11 +100,14 @@ def spi_list() -> list[str]: def st_serial_to_dfu_serial(st: str, mcu_type: McuType = McuType.F4): if st is None or st == "none": return None - uid_base = struct.unpack("H" * 6, bytes.fromhex(st)) - if mcu_type == McuType.H7: - return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4], uid_base[3])).upper().decode("utf-8") - else: - return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4] + 0xA, uid_base[3])).upper().decode("utf-8") + try: + uid_base = struct.unpack("H" * 6, bytes.fromhex(st)) + if mcu_type == McuType.H7: + return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4], uid_base[3])).upper().decode("utf-8") + else: + return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4] + 0xA, uid_base[3])).upper().decode("utf-8") + except struct.error: + return None def get_mcu_type(self) -> McuType: return self._mcu_type diff --git a/python/spi.py b/python/spi.py index 5be1e56b6b..2b086dc2b3 100644 --- a/python/spi.py +++ b/python/spi.py @@ -402,7 +402,8 @@ def get_bootloader_id(self): def get_chip_id(self) -> int: r = self._cmd(0x02, read_bytes=3) - assert r[0] == 1 # response length - 1 + if r[0] != 1: # response length - 1 + raise PandaSpiException("incorrect response length") return ((r[1] << 8) + r[2]) def go_cmd(self, address: int) -> None: From 4a11d52e07c9dff5f48d4fd20397f2612786ea8f Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Thu, 21 Nov 2024 11:04:09 -0800 Subject: [PATCH 27/33] Control over FD flag of CAN messages (#2085) * Control over FD flag of CAN messages * misra --- board/can_declarations.h | 2 +- board/drivers/bxcan.h | 3 +++ board/drivers/can_common.h | 10 +++++----- board/drivers/can_common_declarations.h | 1 + board/drivers/fdcan.h | 15 +++++++++++---- board/main_comms.h | 4 ++++ python/__init__.py | 15 +++++++++------ tests/libpanda/libpanda_py.py | 2 +- 8 files changed, 35 insertions(+), 17 deletions(-) diff --git a/board/can_declarations.h b/board/can_declarations.h index 569e2939ac..878d9c77e2 100644 --- a/board/can_declarations.h +++ b/board/can_declarations.h @@ -13,7 +13,7 @@ #endif typedef struct { - unsigned char reserved : 1; + unsigned char fd : 1; unsigned char bus : 3; unsigned char data_len_code : 4; // lookup length with dlc_to_len unsigned char rejected : 1; diff --git a/board/drivers/bxcan.h b/board/drivers/bxcan.h index e4c72c61aa..721a7ee492 100644 --- a/board/drivers/bxcan.h +++ b/board/drivers/bxcan.h @@ -89,6 +89,7 @@ void process_can(uint8_t can_number) { if ((CANx->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) { if ((CANx->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) { CANPacket_t to_push; + to_push.fd = 0U; to_push.returned = 1U; to_push.rejected = 0U; to_push.extended = (CANx->sTxMailBox[0].TIR >> 2) & 0x1U; @@ -144,6 +145,7 @@ void can_rx(uint8_t can_number) { // add to my fifo CANPacket_t to_push; + to_push.fd = 0U; to_push.returned = 0U; to_push.rejected = 0U; to_push.extended = (CANx->sFIFOMailBox[0].RIR >> 2) & 0x1U; @@ -159,6 +161,7 @@ void can_rx(uint8_t can_number) { if (bus_fwd_num != -1) { CANPacket_t to_send; + to_send.fd = 0U; to_send.returned = 0U; to_send.rejected = 0U; to_send.extended = to_push.extended; // TXRQ diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 5a074e775c..0f24a77c1b 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -131,10 +131,10 @@ void can_clear(can_ring *q) { // Helpers // Panda: Bus 0=CAN1 Bus 1=CAN2 Bus 2=CAN3 bus_config_t bus_config[BUS_CONFIG_ARRAY_SIZE] = { - { .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 }, + { .bus_lookup = 0U, .can_num_lookup = 0U, .forwarding_bus = -1, .can_speed = 5000U, .can_data_speed = 20000U, .canfd_auto = false, .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_auto = false, .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_auto = false, .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_auto = false, .canfd_enabled = false, .brs_enabled = false, .canfd_non_iso = false }, }; void can_init_all(void) { @@ -165,7 +165,7 @@ void ignition_can_hook(CANPacket_t *to_push) { if (bus == 0) { int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); - + // GM exception if ((addr == 0x1F1) && (len == 8)) { // SystemPowerMode (2=Run, 3=Crank Request) diff --git a/board/drivers/can_common_declarations.h b/board/drivers/can_common_declarations.h index 1a3518c2a8..57bf87d9c7 100644 --- a/board/drivers/can_common_declarations.h +++ b/board/drivers/can_common_declarations.h @@ -13,6 +13,7 @@ typedef struct { int8_t forwarding_bus; uint32_t can_speed; uint32_t can_data_speed; + bool canfd_auto; bool canfd_enabled; bool brs_enabled; bool canfd_non_iso; diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index 0144aa17c2..eaf50caaab 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -100,7 +100,11 @@ void process_can(uint8_t can_number) { fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0U) ? (to_send.addr) : (to_send.addr << 18)); - uint32_t canfd_enabled_header = bus_config[can_number].canfd_enabled ? (1UL << 21) : 0UL; + + // If canfd_auto is set, outgoing packets will be automatically sent as CAN-FD if an incoming CAN-FD packet was seen + bool fd = bus_config[can_number].canfd_auto ? bus_config[can_number].canfd_enabled : (bool)(to_send.fd > 0U); + uint32_t canfd_enabled_header = fd ? (1UL << 21) : 0UL; + uint32_t brs_enabled_header = bus_config[can_number].brs_enabled ? (1UL << 20) : 0UL; fifo->header[1] = (to_send.data_len_code << 16) | canfd_enabled_header | brs_enabled_header; @@ -115,6 +119,7 @@ void process_can(uint8_t can_number) { // Send back to USB CANPacket_t to_push; + to_push.fd = fd; to_push.returned = 1U; to_push.rejected = 0U; to_push.extended = to_send.extended; @@ -168,6 +173,10 @@ void can_rx(uint8_t can_number) { // getting address fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); + bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); + bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); + + to_push.fd = canfd_frame; to_push.returned = 0U; to_push.rejected = 0U; to_push.extended = (fifo->header[0] >> 30) & 0x1U; @@ -175,9 +184,6 @@ void can_rx(uint8_t can_number) { to_push.bus = bus_number; to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); - bool canfd_frame = ((fifo->header[1] >> 21) & 0x1U); - bool brs_frame = ((fifo->header[1] >> 20) & 0x1U); - uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); data_len_w += ((dlc_to_len[to_push.data_len_code] % 4U) > 0U) ? 1U : 0U; for (unsigned int i = 0; i < data_len_w; i++) { @@ -193,6 +199,7 @@ void can_rx(uint8_t can_number) { if (bus_fwd_num != -1) { CANPacket_t to_send; + to_send.fd = to_push.fd; to_send.returned = 0U; to_send.rejected = 0U; to_send.extended = to_push.extended; diff --git a/board/main_comms.h b/board/main_comms.h index 7d754d050f..6cc2e429c3 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -317,6 +317,10 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { case 0xe7: set_power_save_state(req->param1); break; + // **** 0xe8: set can-fd auto swithing mode + case 0xe8: + bus_config[req->param1].canfd_auto = req->param2 > 0U; + break; // **** 0xf1: Clear CAN ring buffer. case 0xf1: if (req->param1 == 0xFFFFU) { diff --git a/python/__init__.py b/python/__init__.py index 14d324ab93..d6d3ebc594 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -31,7 +31,7 @@ def calculate_checksum(data): res ^= b return res -def pack_can_buffer(arr): +def pack_can_buffer(arr, fd=False): snds = [b''] for address, dat, bus in arr: assert len(dat) in LEN_TO_DLC @@ -41,7 +41,7 @@ def pack_can_buffer(arr): data_len_code = LEN_TO_DLC[len(dat)] header = bytearray(CANPACKET_HEAD_SIZE) word_4b = address << 3 | extended << 2 - header[0] = (data_len_code << 4) | (bus << 1) + header[0] = (data_len_code << 4) | (bus << 1) | int(fd) header[1] = word_4b & 0xFF header[2] = (word_4b >> 8) & 0xFF header[3] = (word_4b >> 16) & 0xFF @@ -807,6 +807,9 @@ def set_can_data_speed_kbps(self, bus, speed): def set_canfd_non_iso(self, bus, non_iso): self._handle.controlWrite(Panda.REQUEST_OUT, 0xfc, bus, int(non_iso), b'') + def set_canfd_auto(self, bus, auto): + self._handle.controlWrite(Panda.REQUEST_OUT, 0xe8, bus, int(auto), b'') + def set_uart_baud(self, uart, rate): self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, int(rate / 300), b'') @@ -828,15 +831,15 @@ def can_reset_communications(self): self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'') @ensure_can_packet_version - def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS): - snds = pack_can_buffer(arr) + def can_send_many(self, arr, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS): + snds = pack_can_buffer(arr, fd=fd) for tx in snds: while len(tx) > 0: bs = self._handle.bulkWrite(3, tx, timeout=timeout) tx = tx[bs:] - def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS): - self.can_send_many([[addr, dat, bus]], timeout=timeout) + def can_send(self, addr, dat, bus, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS): + self.can_send_many([[addr, dat, bus]], fd=fd, timeout=timeout) @ensure_can_packet_version def can_recv(self): diff --git a/tests/libpanda/libpanda_py.py b/tests/libpanda/libpanda_py.py index 6f888ac60e..d24640b9dc 100644 --- a/tests/libpanda/libpanda_py.py +++ b/tests/libpanda/libpanda_py.py @@ -12,7 +12,7 @@ ffi.cdef(""" typedef struct { - unsigned char reserved : 1; + unsigned char fd : 1; unsigned char bus : 3; unsigned char data_len_code : 4; unsigned char rejected : 1; From 2fbf0c5ff86f2e94021d7c937b9f8eb6839af484 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 21 Nov 2024 11:07:57 -0800 Subject: [PATCH 28/33] fix indendation --- board/main_comms.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/main_comms.h b/board/main_comms.h index 6cc2e429c3..e366b19a09 100644 --- a/board/main_comms.h +++ b/board/main_comms.h @@ -319,8 +319,8 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) { break; // **** 0xe8: set can-fd auto swithing mode case 0xe8: - bus_config[req->param1].canfd_auto = req->param2 > 0U; - break; + bus_config[req->param1].canfd_auto = req->param2 > 0U; + break; // **** 0xf1: Clear CAN ring buffer. case 0xf1: if (req->param1 == 0xFFFFU) { From b770745342dc52fe30434763532e4c73887c71ca Mon Sep 17 00:00:00 2001 From: Maxime Desroches Date: Thu, 21 Nov 2024 11:48:02 -0800 Subject: [PATCH 29/33] ci: split MISRA linter and MISRA mutation (#2086) * sep * this --- .github/workflows/test.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 367cf2dc36..52c2a06103 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -88,8 +88,8 @@ jobs: scons -j$(nproc) ${{ matrix.flags }} && \ tests/safety/test.sh" - misra: - name: MISRA C:2012 + misra_linter: + name: MISRA C:2012 Linter runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -101,6 +101,17 @@ jobs: - name: Run MISRA C:2012 analysis timeout-minutes: 2 run: ${{ env.RUN }} "cd tests/misra && ./test_misra.sh" + + misra_mutation: + name: MISRA C:2012 Mutation + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Build FW + run: ${{ env.RUN }} "scons -j$(nproc)" - name: MISRA mutation tests timeout-minutes: 5 run: ${{ env.RUN }} "cd tests/misra && pytest -n8 test_mutation.py" From cfbc3ff835d711c5afc733c964b5283ff5a998c6 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Fri, 22 Nov 2024 10:29:11 -0800 Subject: [PATCH 30/33] Ensure auto switching FD is off in Python (#2087) --- python/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/__init__.py b/python/__init__.py index d6d3ebc594..88ccf897a1 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -324,6 +324,10 @@ def connect(self, claim=True, wait=False): # reset comms self.can_reset_communications() + # disable automatic CAN-FD switching + for bus in range(PANDA_BUS_CNT): + self.set_canfd_auto(bus, False) + # set CAN speed for bus in range(PANDA_BUS_CNT): self.set_can_speed_kbps(bus, self._can_speed_kbps) From 5761ab58ea36beba2ddf07709590b591e04312bd Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Fri, 22 Nov 2024 19:33:04 -0800 Subject: [PATCH 31/33] what was that doing there? --- board/recover.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/board/recover.py b/board/recover.py index c149e948d1..6268a07d96 100755 --- a/board/recover.py +++ b/board/recover.py @@ -2,21 +2,15 @@ import os import time import subprocess -import argparse from panda import Panda, PandaDFU board_path = os.path.dirname(os.path.realpath(__file__)) if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--all", action="store_true", help="Recover all Panda devices") - args = parser.parse_args() - subprocess.check_call(f"scons -C {board_path}/.. -j$(nproc) {board_path}", shell=True) - serials = Panda.list() if args.all else [None] - for s in serials: + for s in Panda.list(): with Panda(serial=s) as p: print(f"putting {p.get_usb_serial()} in DFU mode") p.reset(enter_bootstub=True) From 4dc203376afbd20914373ec721610d20a851add5 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Fri, 29 Nov 2024 17:37:33 +0100 Subject: [PATCH 32/33] more open drains (#2091) also open drain Co-authored-by: Comma Device --- board/boards/cuatro.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 4a6b5da096..3d87318b02 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -87,6 +87,10 @@ static void cuatro_init(void) { set_gpio_output_type(GPIOB, 2, OUTPUT_TYPE_OPEN_DRAIN); set_gpio_output_type(GPIOD, 15, OUTPUT_TYPE_OPEN_DRAIN); + // more open drain + set_gpio_output_type(GPIOD, 3, OUTPUT_TYPE_OPEN_DRAIN); // FAN_EN + set_gpio_output_type(GPIOC, 12, OUTPUT_TYPE_OPEN_DRAIN); // VBAT_EN + // Power readout set_gpio_mode(GPIOC, 5, MODE_ANALOG); set_gpio_mode(GPIOA, 6, MODE_ANALOG); From c7cc2deaf046403899b5bcc964b9f48bfd508534 Mon Sep 17 00:00:00 2001 From: Robbe Derks Date: Fri, 29 Nov 2024 17:41:03 +0100 Subject: [PATCH 33/33] Cuatro: no ir (#2093) no ir --- board/boards/cuatro.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/boards/cuatro.h b/board/boards/cuatro.h index 3d87318b02..90d0627517 100644 --- a/board/boards/cuatro.h +++ b/board/boards/cuatro.h @@ -169,7 +169,7 @@ board board_cuatro = { .read_voltage_mV = cuatro_read_voltage_mV, .read_current_mA = cuatro_read_current_mA, .set_fan_enabled = cuatro_set_fan_enabled, - .set_ir_power = tres_set_ir_power, + .set_ir_power = unused_set_ir_power, .set_siren = cuatro_set_siren, .set_bootkick = cuatro_set_bootkick, .read_som_gpio = tres_read_som_gpio,