Source code for ska_low_cbf_fpga.ami_info

# -*- coding: utf-8 -*-
#
# Copyright (c) 2025 CSIRO Space and Astronomy.
#
# Distributed under the terms of the CSIRO Open Source Software Licence Agreement.
# See LICENSE for more info.
from pathlib import Path

from ska_low_cbf_fpga.hardware_info import FpgaHardwareInfo
from ska_low_cbf_fpga.icl_field import IclField


[docs] def _int_from_mac_str(mac) -> int: """ Convert MAC address str to int. :param mac: MAC address string that may contain seperator characters e.g. "00:01:02:03:04:05" """ # we see ':' used as seperator, but let's handle other possibilities return int(mac.translate(str.maketrans("", "", ":.- ")), 16)
[docs] class AmiInfo(FpgaHardwareInfo): """ Hardware monitoring for AMI devices via sysfs. """
[docs] def __init__(self, hwmon_dir: Path | str, sys_dir_0: Path | str): if isinstance(hwmon_dir, str): hwmon_dir = Path(hwmon_dir) if isinstance(sys_dir_0, str): sys_dir_0 = Path(sys_dir_0) self._hwmon_dir = hwmon_dir self._sys_dir = sys_dir_0
[docs] def _read_hw(self, filename: Path | str): """Read text from a file in our hwmon directory.""" return self._hwmon_dir.joinpath(filename).read_text(encoding="ascii").strip()
[docs] def _read_sys(self, filename: Path | str): """Read text from a file in our sysfs directory.""" return self._sys_dir.joinpath(filename).read_text(encoding="ascii").strip()
@property def fpga_power(self) -> IclField[float]: """ Get FPGA power consumption in Watts. """ power = int(self._read_hw("power1_input")) / 1e6 return IclField( description="FPGA power consumption in W", format="%f", type_=float, value=power, user_write=False, ) @property def fpga_temperature(self) -> IclField[int]: """ Get FPGA temperature in degrees Celsius. """ temperature = int(self._read_hw("temp2_input")) / 1e3 return IclField( description="FPGA temperature in °C", format="%d", type_=int, value=temperature, user_write=False, ) @property def hbm_temperature(self) -> IclField[int]: """ Get HBM temperature in degrees Celsius. """ # this is called "1V2_VCC_HBM", # not sure if it's a voltage regulator or the memory itself, # but it's the closest we seem to have temperature = int(self._read_hw("temp9_input")) / 1e3 return IclField( description="HBM temperature in °C", format="%d", type_=int, value=temperature, user_write=False, ) @property def mac_addresses(self) -> IclField[str]: """ Get Ethernet MAC addresses. :returns: Array of str, formatted like "00:01:02:03:04:05". """ first_mac = _int_from_mac_str(self._read_sys("mac_addr")) n_macs = int(self._read_sys("mac_addr_count")) last_mac = _int_from_mac_str(self._read_sys("mac_addr_n")) macs = [] for mac_int in range(first_mac, first_mac + n_macs): mac_hex = f"{mac_int:012x}" macs.append(":".join(mac_hex[i : i + 2] for i in range(0, 12, 2))) assert len(macs) == n_macs assert last_mac == _int_from_mac_str(macs[-1]) return IclField( description="Ethernet MAC addresses", format="%s", type_=str, value=macs, length=len(macs), user_write=False, ) @property def pcie_12v_current(self) -> IclField[float]: """ Get PCIe 12V power rail's current. """ current = int(self._read_hw("curr7_input")) / 1e3 return IclField( description="PCIe 12V current in Amperes", format="%f", type_=float, value=current, user_write=False, ) @property def pcie_12v_voltage(self) -> IclField[float]: """ Get PCIe 12V power rail voltage reading. """ voltage = int(self._read_hw("in6_input")) / 1e3 return IclField( description="PCIe 12V power rail in volts", format="%f", type_=float, value=voltage, user_write=False, ) @property def power_supply_12v_current(self) -> IclField[float]: """ Get 12V AUX total current (sum of two). """ current_1 = int(self._read_hw("curr3_input")) / 1e3 current_2 = int(self._read_hw("curr4_input")) / 1e3 return IclField( description="Total AUX 12V current in Amperes", format="%f", type_=float, value=current_1 + current_2, user_write=False, ) @property def power_supply_12v_voltage(self) -> IclField[float]: """ Get 12V AUX voltage (one of the two that deviates furthest from 12V). """ voltage_1 = int(self._read_hw("in2_input")) / 1e3 voltage_2 = int(self._read_hw("in3_input")) / 1e3 v1_error = abs(12 - voltage_1) v2_error = abs(12 - voltage_2) voltage = voltage_1 if v1_error > v2_error else voltage_2 return IclField( description="Most deviant AUX 12 voltage in volts", format="%f", type_=float, value=voltage, user_write=False, )