From d86686fffda05d558a4c103ca835bb3da231b7a3 Mon Sep 17 00:00:00 2001 From: Daniel Koepping Date: Wed, 5 Aug 2026 07:21:58 -0700 Subject: [PATCH] log chestnut metrics (#38527) * log chestnut state * chestnut metrics * more metrics * fix chestnut metrics * cache chestnut power limit * use valid * move to class --- openpilot/cereal/log.capnp | 11 +++++++ openpilot/cereal/services.py | 1 + openpilot/selfdrive/modeld/modeld.py | 44 +++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/openpilot/cereal/log.capnp b/openpilot/cereal/log.capnp index 10b968b039..9c04599843 100644 --- a/openpilot/cereal/log.capnp +++ b/openpilot/cereal/log.capnp @@ -707,6 +707,16 @@ struct UsbState { } } +struct ChestnutState { + tempC @0 :Float32; + memoryTempC @1 :Float32; + powerDrawW @2 :Float32; + powerLimitW @3 :Float32; + gpuUsagePercent @4 :UInt8; + gpuClockMhz @5 :UInt16; + fanSpeedRpm @6 :UInt16; +} + struct RadarState @0x9a185389d6fdd05f { mdMonoTime @6 :UInt64; # for debugging radarErrors @13 :Car.RadarData.Error; @@ -2570,6 +2580,7 @@ struct Event { procLog @33 :ProcLog; clocks @35 :Clocks; deviceState @6 :DeviceState; + chestnutState @152 :ChestnutState; logMessage @18 :Text; errorLogMessage @85 :Text; diff --git a/openpilot/cereal/services.py b/openpilot/cereal/services.py index 42669688b0..a41a890f97 100755 --- a/openpilot/cereal/services.py +++ b/openpilot/cereal/services.py @@ -25,6 +25,7 @@ _services: dict[str, tuple] = { "accelerometer": (True, 104., 104), "temperatureSensor": (True, 2., 200), "deviceState": (True, 2., 1), + "chestnutState": (True, 0.1, 1), "touch": (True, 20., 1), "can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment "controlsState": (True, 100., 10, QueueSize.MEDIUM), diff --git a/openpilot/selfdrive/modeld/modeld.py b/openpilot/selfdrive/modeld/modeld.py index d7ddce60b9..8b2bb40d12 100755 --- a/openpilot/selfdrive/modeld/modeld.py +++ b/openpilot/selfdrive/modeld/modeld.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 +from functools import cached_property import os os.environ['GMMU'] = '0' # for usbgpu fast loading, noop for qcom from tinygrad.tensor import Tensor +from tinygrad.device import Device import threading import time import numpy as np @@ -9,6 +11,7 @@ import openpilot.cereal.messaging as messaging from openpilot.cereal import log from opendbc.car.structs import car from openpilot.cereal.messaging import PubMaster, SubMaster +from openpilot.cereal.services import SERVICE_LIST from msgq.visionipc import VisionIpcClient, VisionStreamType, VisionBuf from opendbc.car.car_helpers import get_demo_car_params from openpilot.common.swaglog import cloudlog @@ -64,6 +67,40 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log. shouldStop=bool(stop)) +class ChestnutState: + # only modeld can access chestnut + def __init__(self, pm: PubMaster): + self.pm = pm + self.valid = True + + @cached_property + def power_limit(self) -> int: + smu = Device["AMD"].iface.dev_impl.smu + return smu._send_msg(smu.smu_mod.PPSMC_MSG_GetPptLimit, 0, read_back_arg=True) + + def send(self) -> None: + msg = messaging.new_message('chestnutState') + state = msg.chestnutState + try: + smu = Device["AMD"].iface.dev_impl.smu + metrics = smu.read_table(smu.smu_mod.SmuMetricsExternal_t, smu.smu_mod.TABLE_SMU_METRICS).SmuMetrics + state.tempC = metrics.AvgTemperature[smu.smu_mod.TEMP_HOTSPOT] + state.memoryTempC = metrics.AvgTemperature[smu.smu_mod.TEMP_MEM] + state.powerDrawW = metrics.AverageSocketPower + state.powerLimitW = self.power_limit + state.gpuUsagePercent = metrics.AverageGfxActivity + state.gpuClockMhz = metrics.AverageGfxclkFrequencyPostDs + state.fanSpeedRpm = metrics.AvgFanRpm + self.valid = True + except Exception: + if self.valid: + cloudlog.exception("chestnut state read failed") + self.valid = False + + msg.valid = self.valid + self.pm.send('chestnutState', msg) + + class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -204,11 +241,13 @@ def main(demo=False): cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting") # messaging - pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) + pub_socks = ["modelV2", "drivingModelData", "cameraOdometry"] + (["chestnutState"] if USBGPU else []) + pm = PubMaster(pub_socks) sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) publish_state = PublishState() params = Params() + chestnut_state = ChestnutState(pm) if USBGPU else None # setup filter to track dropped frames frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_RUN_FREQ) @@ -353,6 +392,9 @@ def main(demo=False): pm.send('cameraOdometry', posenet_send) last_vipc_frame_id = meta_main.frame_id + if chestnut_state is not None and run_count % round(ModelConstants.MODEL_RUN_FREQ / SERVICE_LIST['chestnutState'].frequency) == 0: + chestnut_state.send() + if __name__ == "__main__": try: