From f24c7931f1065da1f472014aaa22678e3e5d3d80 Mon Sep 17 00:00:00 2001 From: xz_ocs Date: Fri, 16 Jan 2026 10:36:56 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20'ocs/0116/ocsdiag.py'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ocs/0116/ocsdiag.py | 2874 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2874 insertions(+) create mode 100644 ocs/0116/ocsdiag.py diff --git a/ocs/0116/ocsdiag.py b/ocs/0116/ocsdiag.py new file mode 100644 index 0000000..1a3c377 --- /dev/null +++ b/ocs/0116/ocsdiag.py @@ -0,0 +1,2874 @@ +import logging, argparse, time, sys, math, os +import yaml, json +from typing import List, Optional, Tuple +from pathlib import Path +import pandas as pd +from datetime import datetime +import pdb +import subprocess +import concurrent.futures +from concurrent.futures import ThreadPoolExecutor +import matplotlib.pyplot as plt +sys.path.append('C:\\ws\\tmp\\NexusBench-baihe-br\\nexusbench') +sys.path.append(os.path.dirname(__file__)) + +from parser.topology_parser import TopoMappingParser +from parser.ibias_rssi_map_parser import IbiasRssiMapParser +from toolbox.rssi_ibias_test import find_best_ibias_for_expected_rssi,find_best_ibias_for_expected_rssi_old +from gpu.biren.exp_util import DevWhiteRiverExp +from gpu.biren.smbus_http_util import SmbusHttpUtil +from gpu.biren.vuart_util import VuartUtil +from log.logger import Logger +from parser.transceiver_config_parser import TransceiverConfigParser, RegisterInfo +from toolbox.mgc_tuning_tool import MgcTuningTool, LaneErrInfo +import zlib +from functools import partial +from toolbox.regs_save_load_tool import LaneRegInfo, RouteInfo, SlotInfo, ExpInfo, HostRegInfo, OptRegValueManager +from toolbox.lowfreq_tuning_tool import LowFreqTuningTool +from toolbox.eq_tune_tool import EqTuneTool, RetimerLaneSetting +from toolbox.recv_ce_tool import RetimerLinkState, RecvCeTool +from toolbox.opt_reg_access_tool import OptRegAccessTool +from toolbox.prbs_tool import PrbsTool +print(zlib.ZLIB_VERSION) + + +def Authentication(bmc: DevWhiteRiverExp, exp_id: int, slot_id: int): + bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0, 0x7a, 4, 'EA987654') + bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0, 0xE9, 1, '04') + # bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0xB4, 0x88, 1, '01') + +def ReadModuleType(bmc: DevWhiteRiverExp, exp_id: int, slot_id: int): + res_data = bmc.GetOpticalModuleRegs(exp_id, slot_id, 0,0xB0, 0xB0, 9) + logging.debug(res_data) + bytes_data = bytes.fromhex(res_data) + type = bytes_data.decode('ascii', errors='ignore') + logging.debug(f'----------------SLOT: {slot_id}, Module type:{type}') + return type + +def ExpColdReset(bmc: DevWhiteRiverExp, exp_id: int): + ret = bmc.ExpColdReset(exp_id) + logging.info(f'Exp cold reset, exp id: {exp_id}, result: {ret}') + +def AutoTuneMGC(host: str, bmc: DevWhiteRiverExp, remote_bmc: DevWhiteRiverExp, + reg_tool: OptRegAccessTool, remote_reg_access_tool: OptRegAccessTool, + exp_id: int, slot_list: List[int], lane_list: List[int], + reg_table: TransceiverConfigParser, cmd: str, route_name: str): + + logging.info(f"start auto tune MGC: exp_id={exp_id}, slots={slot_list}") + + if rtmr_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in rtmr_list] + + if remote_bmc is None: + remote_bmc = bmc + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_tool, remote_reg_access_tool, route_name) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + topo_map.parse() + + logging.info('-------------step 3:') + mgc_tool = MgcTuningTool(host, reg_table, reg_tool, remote_reg_access_tool, bmc, topo_map, prbs_tool, route_name) + + remote_mgc_tool = None + if remote_bmc != bmc: + remote_prbs_tool = PrbsTool(remote_bmc, bmc, remote_reg_access_tool, reg_tool, route_name) + remote_mgc_tool = MgcTuningTool(host, reg_table, remote_reg_access_tool, reg_tool, remote_bmc, topo_map, remote_prbs_tool, route_name) + + logging.info('-------------step 4:') + # 对每个slot执行自动调优 + # if route_name == '786-1-oneta' or route_name == '786-1-onoc2': + if cmd == 'mgc-auto-tune2': + + mgc_tool.auto_tune2(exp_id, retimers) + elif cmd == 'tiapeak-auto-tune': + mgc_tool.auto_tia_peak_tune(exp_id, retimers) + elif cmd == 'lowfreq-auto-tune': + mgc_tool.auto_lowfreq_tune(exp_id, retimers) + elif cmd == 'highfreq-auto-tune': + mgc_tool.auto_highfreq_tune(exp_id, retimers) + elif cmd == 'opcurrent-auto-tune': + mgc_tool.auto_opcurrent_tune_test(exp_id, retimers) + + elif cmd == 'opcurrent-auto-tune-test': + mgc_tool.auto_ibias_tune2(exp_id, retimers) + + elif cmd == 'rt-ibias': + mgc_tool.rt_ibias(exp_id, retimers) + + elif cmd == 'plot-mgc-vpeak': + prbs_tool.ResetRetimerAndEnablePrbs(exp_id, retimers, 9) + for slot in slot_list: + mgc_tool.debug_calc_target_vpeak_new(exp_id, slot) + + if cmd == 'mgc-auto-tune': + + prbs_tool.ResetRetimerAndEnablePrbs(exp_id, retimers, 9) + + logging.info(f"---------- slot_list: {slot_list}") + for slot_id in slot_list: + if cmd == 'mgc-auto-tune': + mgc_tool.auto_tune(exp_id, slot_id, lane_list) + + if remote_mgc_tool is not None: + remote_mgc_tool.auto_tune(exp_id, slot_id, lane_list) + + # mgc_tool2 = MgcTuningTool(host, reg_table, reg_tool, remote_reg_access_tool, bmc, topo_map, prbs_tool, route_name) + # mgc_tool.auto_tune(exp_id, slot_id, lane_list) + elif cmd == 'mgc-manual-tune': + mgc_tool.manual_tune(exp_id, slot_id) + elif cmd == 'mgc-calc-target-vpeak': + mgc_tool.calc_target_vpeak_new(exp_id, slot_id, lane_list) + +def AutoTuneLowFreq(host: str, bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, reg_table_file: str, cmd: str): + + logging.info(f"start auto tune low freq: exp_id={exp_id}, slots={slot_list}") + + # 加载寄存器表 + reg_table = TransceiverConfigParser(reg_table_file) + + # 创建Low Freq 调优工具实例 + lowfreq_tool = LowFreqTuningTool(host, reg_table, bmc) + logging.info('-------------step 4:') + # 对每个slot执行自动调优 + for slot_id in slot_list: + lowfreq_tool.auto_tune(exp_id, slot_id) + + +def TuneHighFreq(reg_tool: OptRegAccessTool, exp_id: int): + # 3500 ~ 5500 + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [1,3,5], ['tia_peak'], '150') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,2,4,6,7], ['tia_peak'], '112') + + + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [3,4], ['highfreq_eq'], '130') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [2,6,7], ['highfreq_eq'], '50') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0], ['highfreq_eq'], '100') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [1], ['highfreq_eq'], '180') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [5], ['highfreq_eq'], '180') + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [4,7], ['highfreq_eq'], '0') + + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['lowfreq_eq'], '128') + + + # exp_id 需替换为实际值 + # reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['opcurrent'], '140') + # reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['highfreq_eq'], '200') + # reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['tia_peak'], '112') + # reg_tool.write_opt_regs(exp_id, [0], [3], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [1], [2], ['highfreq_eq'], '50') + # reg_tool.write_opt_regs(exp_id, [1], [5], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [1], [5], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [2], [0], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [2], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [2], [3], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [2], [6], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [2], [7], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [3], [0], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [3], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [3], [2], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [3], [4], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [3], [5], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [3], [6], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [3], [7], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [4], [0], ['highfreq_eq'], '50') + # reg_tool.write_opt_regs(exp_id, [4], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [4], [2], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [4], [3], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [4], [5], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [4], [6], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [4], [7], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [0], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [2], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [3], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [5], [4], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [5], ['highfreq_eq'], '50') + # reg_tool.write_opt_regs(exp_id, [5], [6], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [5], [7], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [6], [0], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [6], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [6], [2], ['highfreq_eq'], '50') + # reg_tool.write_opt_regs(exp_id, [6], [4], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [6], [5], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [6], [6], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [7], [0], ['highfreq_eq'], '50') + # reg_tool.write_opt_regs(exp_id, [7], [1], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [7], [2], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [7], [3], ['highfreq_eq'], '100') + # reg_tool.write_opt_regs(exp_id, [7], [6], ['highfreq_eq'], '100') + + + # all_lane_reg_values: List[LaneRegInfo] = reg_tool.read_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['highfreq_eq']) + # for lane_reg in all_lane_reg_values: + # if lane_reg.highfreq_eq >=200: + # reg_tool.write_opcurrent_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, 150) + # elif lane_reg.highfreq_eq >=100: + # reg_tool.write_opcurrent_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, 160) + # elif lane_reg.highfreq_eq >=50: + # reg_tool.write_opcurrent_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, 165) + # elif lane_reg.highfreq_eq >=0: + # reg_tool.write_opcurrent_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, 170) + # reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['opcurrent'], '138') + + # # < 3000 + # reg_tool.write_opt_regs(exp_id, [2], [4], ['highfreq_eq'], '200') + # # < 3000 -3500 + # reg_tool.write_opt_regs(exp_id, [5,6,7], [4], ['highfreq_eq'], '150') + # reg_tool.write_opt_regs(exp_id, [3,7], [5], ['highfreq_eq'], '150') + # reg_tool.write_opt_regs(exp_id, [5], [1], ['highfreq_eq'], '150') + + # # > 5500 + # reg_tool.write_opt_regs(exp_id, [7], [3], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [0,1], [6], ['highfreq_eq'], '0') + # reg_tool.write_opt_regs(exp_id, [1,5], [2], ['highfreq_eq'], '0') + + +def SaveRegsToFile(reg_tool: OptRegAccessTool, host: str, exp_id: int, + slot_list: List[int], route_name: str,lane_list: List[int], regs: List[str], + save_file_name: str): + + host_reg_info = OptRegValueManager.load_from_yaml(save_file_name) + + if host_reg_info.host != '' and host_reg_info.host != host: + logging.error(f'save_file {save_file_name} is not match!') + return + + if host_reg_info.host == '': + host_reg_info.host = host + + for slot_id in slot_list: + logging.info(f'------------------2: regs: {regs}') + + laneRegValueList = reg_tool.read_opt_registers_by_slot(exp_id, slot_id, lane_list, regs) + logging.info(f'------------------3: laneRegValueList: {laneRegValueList}') + + # --- Find or Create ExpInfo --- + target_exp: Optional[ExpInfo] = None + for exp in host_reg_info.exps: + if exp.exp_id == exp_id: + target_exp = exp + break + + if target_exp is None: + target_exp = ExpInfo(exp_id=exp_id) + host_reg_info.exps.append(target_exp) + logging.info(f"Created new ExpInfo for exp_id {exp_id}") + + # --- Find or Create SlotInfo within ExpInfo --- + target_slot: Optional[SlotInfo] = None + for slot in target_exp.slots: + if slot.slot_id == slot_id: + target_slot = slot + break + + if target_slot is None: + target_slot = SlotInfo(slot_id=slot_id) + target_exp.slots.append(target_slot) + logging.info(f"Created new SlotInfo for slot_id {slot_id}") + + + if route_name == '': + logging.error(f'route name is empty!') + exit(0) + + # --- Find or Update/Create RouteInfo within SlotInfo --- + route_found = False + for route in target_slot.modes: + if route.mode_name == route_name: + route.lanes = laneRegValueList # Overwrite existing lanes + route_found = True + logging.info(f"Updated lanes for existing RouteInfo '{route_name}'") + break # Exit the loop once found and updated + + if not route_found: + # If the route was not found, create a new one and add it + new_route = RouteInfo(mode_name=route_name, lanes=laneRegValueList) + target_slot.modes.append(new_route) + logging.info(f"Added new RouteInfo '{route_name}' with lanes") + + # --- Save the updated host_reg_info --- + OptRegValueManager.save_to_yaml(host_reg_info, save_file_name) + + +def LoadRegsToFile(reg_tool: OptRegAccessTool, host: str, exp_id: int, slot_list: List[int], + route_name: str, regs: List[str], load_file_name: str): + + logging.info(f'-----------slot_list: {slot_list}') + + host_reg_info = OptRegValueManager.load_from_yaml(load_file_name) + # logging.info(host_reg_info) + if host_reg_info.host != host or host_reg_info.host == '': + logging.error(f'load_file {load_file_name} is not match!') + return + + for slot_id in slot_list: + logging.info(f'------------------2: regs: {regs}') + + for exp in host_reg_info.exps: + if exp_id == exp.exp_id: + for save_slot in exp.slots: + if slot_id == save_slot.slot_id: + for save_mode in save_slot.modes: + if route_name == save_mode.mode_name: + for lane_info in save_mode.lanes: + + params_to_write = { + 'mgc': lane_info.mgc, + # 'vpeak': lane_info.vpeak, + 'tia_peak': lane_info.tia_peak, + 'ibias': lane_info.ibias, + 'ipcurrent': lane_info.ipcurrent, + 'opcurrent': lane_info.opcurrent, + 'lowfreq_eq': lane_info.lowfreq_eq, + 'highfreq_eq': lane_info.highfreq_eq, + 'vgc_set': lane_info.vgc_set, + # 'drv_vpeak': lane_info.drv_vpeak + } + + for reg_name, param_value in params_to_write.items(): + if param_value == -1: + continue + + try: + reg_tool.write_opt_reg(exp_id, slot_id, lane_info.logic_lane, param_value, reg_name) + logging.info(f"Written {reg_name}={param_value} to exp={exp.exp_id}, slot={slot_id}, lane={lane_info.logic_lane}") + time.sleep(0.05) + except Exception as e: + print(f"Error writing {reg_name} for lane {lane_info.logic_lane}: {e}") + break + break + break + + +def get_search_values(min_val: int, max_val: int, step: int) -> List[int]: + # 生成所有可能的值 + all_values = list(range(min_val, max_val + 1, step)) + + if not all_values: + return [] + + # 找到中间值的索引 + mid_index = len(all_values) // 2 + mid_val = all_values[mid_index] + + # 从中间开始,交替向左右添加 + search_values = [mid_val] + left_index = mid_index - 1 + right_index = mid_index + 1 + + # 交替从左右两边取值 + take_from_right = True # 先从右边开始(可调整) + while left_index >= 0 or right_index < len(all_values): + if take_from_right and right_index < len(all_values): + search_values.append(all_values[right_index]) + right_index += 1 + elif left_index >= 0: # 如果右边取完了或轮到左边 + search_values.append(all_values[left_index]) + left_index -= 1 + # 切换方向 + take_from_right = not take_from_right + + logging.info(f"Search order for lane {search_values}") + return search_values + + +# def AutoTuneIbias(host: str, bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], +# reg_table_parser: TransceiverConfigParser, route_name: str): + +# ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' +# ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + +# if len(lane_list) == 8: # all, 8 is default value +# lane_list = [0,1,2,3] + +# logging.info(f'lane_list: {lane_list}') + +# substandard_slot = [] + +# for slot_id in slot_list: + +# logging.info(f"start auto tune ibias: exp_id={exp_id}, slots={slot_list}") + +# for ibias_lane in lane_list: +# rssi_lanes = ibias_rssi_map.get_rssi_lane(route_name, ibias_lane) +# if rssi_lanes is None: +# logging.error(f'-----------------rssi lanes is none') +# continue +# # SetPrivateLinkRegister(bmc, host, exp_id, slot_id, [ibias_lane], ['ibias'], reg_table_file, ) +# # 一个一个ibias的校准 + +# ibias_reg = reg_table_parser.get_register_by_logic_lane("ibias", ibias_lane) + +# if not ibias_reg or not ibias_reg.valid_range or \ +# not ibias_reg.step or len(ibias_reg.valid_range) < 2: +# logging.error(f'-----------invalid ibias_reg or valid_range or step for ibias_reg') +# continue + +# min_val = ibias_reg.valid_range[0] +# max_val = ibias_reg.valid_range[1] + +# step = ibias_reg.step +# logging.info(f'-------min_val: {min_val}, max_val: {max_val}, step: {step}') +# #search_values = get_search_values(min_val, max_val, step) +# search_values = list(range(min_val, max_val + 1, step)) +# # search_values.sort(reverse=True) +# logging.info(f'-------search_values: {search_values}') + +# measurements_map1 = [] +# measurements_map2 = [] +# expect_value = -1 +# for ibias_value in search_values: +# value_hex_str = f"{ibias_value:04X}" +# result = bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, +# ibias_reg.offset, 2, value_hex_str) + +# for index, rssi_lane in enumerate(rssi_lanes): +# reg = reg_table_parser.get_register_by_logic_lane("rssi", rssi_lane) +# if not reg or not reg.valid_range: +# logging.error(f'------------- rssi reg or valid range is none') +# continue + +# value = bmc.GetOpticalModuleRegs(exp_id, slot_id, reg.bank, reg.page, reg.offset, 2) +# rssi_value = int(value, 16) +# dbm = round(10 * math.log10(rssi_value / 10000), 3) +# logging.info(f'----------rssi_lane: {rssi_lane}, ibias: {ibias_value}, rssi value: {rssi_value}, dbm: {dbm}') + +# min_val = reg.valid_range[0] +# max_val = reg.valid_range[1] +# expect_value = reg.value +# if expect_value is None: +# expect_value = 7080 +# if rssi_value >= int(min_val) and rssi_value <= int(max_val): +# if index == 0: +# measurements_map1.append((ibias_value, rssi_value)) +# elif index == 1: +# measurements_map2.append((ibias_value, rssi_value)) + +# # logging.info(f'rssi1 map: {measurements_map1}') +# # logging.info(f'rssi1 map: {measurements_map2}') + +# logging.info(f'rssi1 map: {measurements_map1}') +# logging.info(f'rssi2 map: {measurements_map2}') +# result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) +# if result: +# ibias, rssi1, rssi2 = result +# logging.info(f"best ibias: {ibias}") +# logging.info(f" RSSI1: {rssi1}") +# logging.info(f" RSSI2: {rssi2}") + +# value_hex_str = f"{ibias:04X}" +# logging.info(f'set best ibias value') +# result = bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, +# ibias_reg.offset, 2, value_hex_str) +# else: +# logging.error("No ibias value present in both maps was found.") +# substandard_slot.append(slot_id) + +# logging.info("Substandard SLOT:") +# for slot in substandard_slot: +# logging.error(f"exp: {exp_id}, slot: {slot}, MODULE: QDD{slot + 1}") + +def AutoTuneIbiasOnetNew(host: str, bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, + reg_table_file: str, route_name: str, ibias_rssi_map: IbiasRssiMapParser): + reg_table = TransceiverConfigParser(reg_table_file) + + if len(lane_list) == 8: # all, 8 is default value + lane_list = [0,1,2,3] + + logging.info(f'lane_list: {lane_list}') + + substandard_slot = [] + + for slot_id in slot_list: + + logging.info(f"start auto tune ibias: exp_id={exp_id}, slots={slot_list}") + + for ibias_lane in lane_list: + rssi_lanes = ibias_rssi_map.get_rssi_lane(route_name, ibias_lane) + if rssi_lanes is None: + logging.error(f'-----------------rssi lanes is none') + continue + # 一个一个ibias的校准 + + ibias_reg = reg_table.get_register_by_lane("ibias", ibias_lane) + if not ibias_reg or not ibias_reg.valid_range or \ + not ibias_reg.step or len(ibias_reg.valid_range) < 2: + logging.error(f'-----------invalid ibias_reg or valid_range or step for ibias_reg') + continue + + min_val = ibias_reg.valid_range[0] + max_val = ibias_reg.valid_range[1] + + step = ibias_reg.step + logging.info(f'-------min_val: {min_val}, max_val: {max_val}, step: {step}') + #search_values = get_search_values(min_val, max_val, step) + search_values = list(range(min_val, max_val + 1, step)) + # search_values.sort(reverse=True) + logging.info(f'-------search_values: {search_values}') + + measurements_map1 = [] + measurements_map2 = [] + expect_value = -1 + for ibias_value in search_values: + value_hex_str = f"{ibias_value:04X}" + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + time.sleep(0.2) + for index, rssi_lane in enumerate(rssi_lanes): + reg = reg_table.get_register_by_lane("rssi", rssi_lane) + if not reg or not reg.valid_range: + logging.error(f'------------- rssi reg or valid range is none') + continue + value = bmc.GetOpticalModuleRegs(exp_id, slot_id, reg.bank, reg.page, reg.offset, 2) + rssi_value = int(value, 16) + dbm = round(10 * math.log10(rssi_value / 10000), 3) + logging.info(f'{bmc.m_smbus.server_id}<-{remote_bmc.m_smbus.server_id} slot:{slot_id}, ibias_lane: {ibias_lane}, ibias: {ibias_value}, rssi_lane: {rssi_lane}, rssi value: {rssi_value}, dbm: {dbm}') + + min_val = reg.valid_range[0] + max_val = reg.valid_range[1] + # expect_value = reg.value + # if expect_value is None: + expect_value = 5623 #-2.5, old 6310 (-2.0) + # if rssi_value >= int(min_val) and rssi_value <= int(max_val): + if index == 0: + measurements_map1.append((ibias_value, rssi_value)) + elif index == 1: + measurements_map2.append((ibias_value, rssi_value)) + + # logging.info(f'rssi1 map: {measurements_map1}') + # logging.info(f'rssi1 map: {measurements_map2}') + + logging.info(f'rssi1 map: {measurements_map1}') + logging.info(f'rssi2 map: {measurements_map2}') + dbm = round(10 * math.log10(expect_value / 10000), 3) + logging.info(f'expect_value: {expect_value}, {dbm}dbm') + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + # rssi_val1 = next((r for i, r in measurements_map1 if i == 2047), None) + # rssi_val2 = next((r for i, r in measurements_map2 if i == 2047), None) + # dbm1 = round(10 * math.log10(rssi_val1 / 10000), 3) + # dbm2 = round(10 * math.log10(rssi_val2 / 10000), 3) + # if dbm1 < -4 or dbm2 < -4: + # substandard_slot.append(slot_id) + # break + + # elif dbm1 < -3.5 or dbm2 < -3.5: # -3.5 ~ -4 + # except_dbm = -2.8 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + # elif dbm1 < -2.5 or dbm2 < -2.5: # -2.5 ~ -3.5 + # except_dbm = -2.7 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + # elif dbm1 < -1.5 or dbm2 < -1.5: # -1.5 ~ -2.5 + # except_dbm = -1.5 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + + if result: + ibias, rssi1, rssi2 = result + + if ibias < 2500: + except_dbm = -1.6 + expect_value = int(10000 * (10 ** (except_dbm / 10))) + logging.info(f' < 2500, use except_dbm {except_dbm}, old ibias: {ibias}') + result = find_best_ibias_for_expected_rssi_old(measurements_map1, measurements_map2, expect_value) + ibias, rssi1, rssi2 = result + + if ibias > 2900: + except_dbm = -2.8 + expect_value = int(10000 * (10 ** (except_dbm / 10))) + logging.info(f' > 2900, use except_dbm {except_dbm}, old ibias: {ibias}') + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + ibias, rssi1, rssi2 = result + + logging.info(f"{bmc.m_smbus.server_id}->{remote_bmc.m_smbus.server_id} ibias_lane: {ibias_lane}, best ibias: {ibias}") + logging.info(f"rssi_lane: {rssi_lanes[0]}, RSSI1: {rssi1}") + logging.info(f"rssi_lane: {rssi_lanes[1]} RSSI2: {rssi2}") + + value_hex_str = f"{ibias:04X}" + logging.info(f'set best ibias value') + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + else: + logging.warning(f'default expect_value{expect_value} not match! use 7586(-1.2dbm)') + expect_value = 7586 # -1.2 + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + if result: + ibias, rssi1, rssi2 = result + if ibias <= 2547: + logging.info(f"{bmc.m_smbus.server_id}->{remote_bmc.m_smbus.server_id} ibias_lane: {ibias_lane}, best ibias: {ibias}") + logging.info(f"rssi_lane: {rssi_lanes[0]}, RSSI1: {rssi1}") + logging.info(f"rssi_lane: {rssi_lanes[1]} RSSI2: {rssi2}") + + value_hex_str = f"{ibias:04X}" + logging.info(f'set best ibias value') + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + else: + logging.error("No ibias value present in both maps was found.") + substandard_slot.append(slot_id) + else: + logging.error("No ibias value present in both maps was found.") + substandard_slot.append(slot_id) + + logging.info("Substandard SLOT:") + for slot in substandard_slot: + logging.error(f"exp: {exp_id}, slot: {slot}, MODULE: QDD{slot + 1}") + +def AutoTuneIbiasOnet(host: str, bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, reg_table_file: str, route_name: str, ibias_rssi_map: IbiasRssiMapParser): + reg_table = TransceiverConfigParser(reg_table_file) + + if len(lane_list) == 8: # all, 8 is default value + lane_list = [0,1,2,3] + + logging.info(f'lane_list: {lane_list}') + + substandard_slot = [] + + + for slot_id in slot_list: + remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0xB4, 0x88, 1, '00') + time.sleep(0.05) + + for slot_id in slot_list: + + logging.info(f"start auto tune ibias: exp_id={exp_id}, slots={slot_list}") + + for ibias_lane in lane_list: + rssi_lanes = ibias_rssi_map.get_rssi_lane(route_name, ibias_lane) + if rssi_lanes is None: + logging.error(f'-----------------rssi lanes is none') + continue + # 一个一个ibias的校准 + + ibias_reg = reg_table.get_register_by_lane("ibias", ibias_lane) + if not ibias_reg or not ibias_reg.valid_range or \ + not ibias_reg.step or len(ibias_reg.valid_range) < 2: + logging.error(f'-----------invalid ibias_reg or valid_range or step for ibias_reg') + continue + + min_val = ibias_reg.valid_range[0] + max_val = ibias_reg.valid_range[1] + + step = ibias_reg.step + logging.info(f'-------min_val: {min_val}, max_val: {max_val}, step: {step}') + #search_values = get_search_values(min_val, max_val, step) + search_values = list(range(min_val, max_val + 1, step)) + # search_values.sort(reverse=True) + logging.info(f'-------search_values: {search_values}') + + measurements_map1 = [] + measurements_map2 = [] + expect_value = -1 + for ibias_value in search_values: + value_hex_str = f"{ibias_value:04X}" + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + time.sleep(0.2) + for index, rssi_lane in enumerate(rssi_lanes): + reg = reg_table.get_register_by_lane("rssi", rssi_lane) + if not reg or not reg.valid_range: + logging.error(f'------------- rssi reg or valid range is none') + continue + value = bmc.GetOpticalModuleRegs(exp_id, slot_id, reg.bank, reg.page, reg.offset, 2) + rssi_value = int(value, 16) + dbm = round(10 * math.log10(rssi_value / 10000), 3) + logging.info(f'{bmc.m_smbus.server_id}<-{remote_bmc.m_smbus.server_id} slot:{slot_id}, ibias_lane: {ibias_lane}, ibias: {ibias_value}, rssi_lane: {rssi_lane}, rssi value: {rssi_value}, dbm: {dbm}') + + min_val = reg.valid_range[0] + max_val = reg.valid_range[1] + # expect_value = reg.value + # if expect_value is None: + expect_value = 5623 #-2.5, old 6310 (-2.0) + # if rssi_value >= int(min_val) and rssi_value <= int(max_val): + if index == 0: + measurements_map1.append((ibias_value, rssi_value)) + elif index == 1: + measurements_map2.append((ibias_value, rssi_value)) + + # logging.info(f'rssi1 map: {measurements_map1}') + # logging.info(f'rssi1 map: {measurements_map2}') + + logging.info(f'rssi1 map: {measurements_map1}') + logging.info(f'rssi2 map: {measurements_map2}') + dbm = round(10 * math.log10(expect_value / 10000), 3) + logging.info(f'expect_value: {expect_value}, {dbm}dbm') + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + # rssi_val1 = next((r for i, r in measurements_map1 if i == 2047), None) + # rssi_val2 = next((r for i, r in measurements_map2 if i == 2047), None) + # dbm1 = round(10 * math.log10(rssi_val1 / 10000), 3) + # dbm2 = round(10 * math.log10(rssi_val2 / 10000), 3) + # if dbm1 < -4 or dbm2 < -4: + # substandard_slot.append(slot_id) + # break + + # elif dbm1 < -3.5 or dbm2 < -3.5: # -3.5 ~ -4 + # except_dbm = -2.8 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + # elif dbm1 < -2.5 or dbm2 < -2.5: # -2.5 ~ -3.5 + # except_dbm = -2.7 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + # elif dbm1 < -1.5 or dbm2 < -1.5: # -1.5 ~ -2.5 + # except_dbm = -1.5 + # expect_value = int(10000 * (10 ** (except_dbm / 10))) + + if result: + ibias, rssi1, rssi2 = result + + if ibias < 2500: + except_dbm = -1.6 + expect_value = int(10000 * (10 ** (except_dbm / 10))) + logging.info(f' < 2500, use except_dbm {except_dbm}, old ibias: {ibias}') + result = find_best_ibias_for_expected_rssi_old(measurements_map1, measurements_map2, expect_value) + ibias, rssi1, rssi2 = result + + if ibias > 2900: + except_dbm = -2.8 + expect_value = int(10000 * (10 ** (except_dbm / 10))) + logging.info(f' > 2900, use except_dbm {except_dbm}, old ibias: {ibias}') + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + ibias, rssi1, rssi2 = result + + logging.info(f"{bmc.m_smbus.server_id}->{remote_bmc.m_smbus.server_id} ibias_lane: {ibias_lane}, best ibias: {ibias}") + logging.info(f"rssi_lane: {rssi_lanes[0]}, RSSI1: {rssi1}") + logging.info(f"rssi_lane: {rssi_lanes[1]} RSSI2: {rssi2}") + + value_hex_str = f"{ibias:04X}" + logging.info(f'set best ibias value') + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + else: + logging.warning(f'default expect_value{expect_value} not match! use 7586(-1.2dbm)') + expect_value = 7586 # -1.2 + result = find_best_ibias_for_expected_rssi(measurements_map1, measurements_map2, expect_value) + if result: + ibias, rssi1, rssi2 = result + if ibias <= 2547: + logging.info(f"{bmc.m_smbus.server_id}->{remote_bmc.m_smbus.server_id} ibias_lane: {ibias_lane}, best ibias: {ibias}") + logging.info(f"rssi_lane: {rssi_lanes[0]}, RSSI1: {rssi1}") + logging.info(f"rssi_lane: {rssi_lanes[1]} RSSI2: {rssi2}") + + value_hex_str = f"{ibias:04X}" + logging.info(f'set best ibias value') + time.sleep(0.05) + result = remote_bmc.SetOpticalModuleRegs(exp_id, slot_id, ibias_reg.bank, ibias_reg.page, + ibias_reg.offset, 2, value_hex_str) + else: + logging.error("No ibias value present in both maps was found.") + substandard_slot.append(slot_id) + else: + logging.error("No ibias value present in both maps was found.") + substandard_slot.append(slot_id) + + logging.info("Substandard SLOT:") + for slot in substandard_slot: + logging.error(f"exp: {exp_id}, slot: {slot}, MODULE: QDD{slot + 1}") + +# reg_access_tool, bmc, host, exp_id, slot_list, ibias_rssi_map, route_name +def RssiCheck(reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, + bmc: DevWhiteRiverExp, host: str, exp_id: int, slot_list: List[int], + ibias_rssi_map: IbiasRssiMapParser, route_name): + + bad_modules = [] + + for slot_id in slot_list: + for ibias_lane in [0, 1, 2, 3]: + ibias_value = remote_reg_tool.read_ibias_reg_by_ibias_lane(exp_id, slot_id, ibias_lane) + + rssi_lanes = ibias_rssi_map.get_rssi_lane(route_name, ibias_lane) + if rssi_lanes is None: + logging.error(f'RSSI lanes not found for exp: {exp_id}, slot: {slot_id}, ibias_lane: {ibias_lane}') + continue + + rssi1_value = reg_tool.read_rssi_reg(exp_id, slot_id, rssi_lanes[0]) + rssi2_value = reg_tool.read_rssi_reg(exp_id, slot_id, rssi_lanes[1]) + dbm1 = round(10 * math.log10(rssi1_value / 10000), 3) + dbm2 = round(10 * math.log10(rssi2_value / 10000), 3) + + if abs(dbm1 - dbm2) > 2.0: + bad_modules.append({ + 'exp_id': exp_id, + 'slot_id': slot_id, + 'ibias_lane': ibias_lane, + 'rssi1_lane': rssi_lanes[0], + 'rssi2_lane': rssi_lanes[1], + 'ibias': ibias_value, + 'dbm1': dbm1, + 'dbm2': dbm2, + 'diff': abs(dbm1 - dbm2) + }) + + if ibias_value > 2800 and ((dbm1 < -3) or (dbm2 < -3)): + bad_modules.append({ + 'exp_id': exp_id, + 'slot_id': slot_id, + 'ibias_lane': ibias_lane, + 'rssi1_lane': rssi_lanes[0], + 'rssi2_lane': rssi_lanes[1], + 'ibias': ibias_value, + 'dbm1': dbm1, + 'dbm2': dbm2, + 'diff': abs(dbm1 - dbm2) + }) + + if ibias_value > 3000 and ((dbm1 < -2.5) or (dbm2 < -2.5)): + bad_modules.append({ + 'exp_id': exp_id, + 'slot_id': slot_id, + 'ibias_lane': ibias_lane, + 'rssi1_lane': rssi_lanes[0], + 'rssi2_lane': rssi_lanes[1], + 'ibias': ibias_value, + 'dbm1': dbm1, + 'dbm2': dbm2, + 'diff': abs(dbm1 - dbm2) + }) + + if bad_modules: + logging.error("❌ Found bad optical modules (|dBm1 - dBm2| > 2.0):") + for mod in bad_modules: + logging.error( + f"host:{host}, exp:{mod['exp_id']}, slot:{mod['slot_id']}," + f"ibias_lane:{mod['ibias_lane']} " + f"ibias:{mod['ibias']} " + f"rssi_lane{mod['rssi1_lane']}={mod['dbm1']}, " + f"rssi_lane{mod['rssi2_lane']}={mod['dbm2']} " + f"(diff:{mod['diff']:.3f}) → BAD" + ) + else: + logging.info("✅ All modules passed RSSI consistency check.") + + +def TiaPeakTune(reg_tool: OptRegAccessTool, bmc: DevWhiteRiverExp, host: str, exp_id: int, slot_list: List[int], + lane_list: List[int], regs: List[str]): + + # all + reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['tia_peak'], '138') + # # > 5500 + # reg_tool.write_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0], ['tia_peak'], '100') + # reg_tool.write_opt_regs(exp_id, [2], [3], ['tia_peak'], '100') + # reg_tool.write_opt_regs(exp_id, [0,1], [2], ['tia_peak'], '100') + # # > 4500 - 5500 + # reg_tool.write_opt_regs(exp_id, [0,1,3,4,5,6,7], [3], ['tia_peak'], '150') + # reg_tool.write_opt_regs(exp_id, [0], [6], ['tia_peak'], '150') + # reg_tool.write_opt_regs(exp_id, [2,3,4,5,6,7], [2], ['tia_peak'], '150') + # # < 3500 + # reg_tool.write_opt_regs(exp_id, [3,4,5], [4], ['tia_peak'], '200') + # reg_tool.write_opt_regs(exp_id, [4,5,6], [5], ['tia_peak'], '200') + # reg_tool.write_opt_regs(exp_id, [0,2,3], [1], ['tia_peak'], '200') + +def MgcTune(reg_tool: OptRegAccessTool, bmc: DevWhiteRiverExp, host: str, exp_id: int, slot_list: List[int], + lane_list: List[int], reg_wt_value): + if reg_wt_value != '': + ext_value = int(reg_wt_value) + else: + ext_value = 5 + + all_lane_reg_values = reg_tool.read_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7], ['mgc']) + # all_lane_reg_values = reg_tool.read_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [1,5], ['mgc']) + # logging.info(f'ret data: {all_lane_reg_values}') + for lane_reg in all_lane_reg_values: + # logging.info(f'------step 1') + if lane_reg.mgc != -1: + # logging.info(f'------step 2') + new_mgc = lane_reg.mgc - ext_value + # logging.info(f'------step 3, new_mgc: {new_mgc}') + reg_tool.write_mgc_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, new_mgc) + # logging.info(f'------step 4') + reg_tool.write_confirm_reg(exp_id, lane_reg.slot_id) + + # all_lane_reg_values = reg_tool.read_opt_regs(exp_id, [0,1,2,3,4,5,6,7], [0], ['mgc']) + # for lane_reg in all_lane_reg_values: + # if lane_reg.mgc != -1: + # new_mgc = lane_reg.mgc + ext_value + # reg_tool.write_mgc_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, new_mgc) + # reg_tool.write_confirm_reg(exp_id, lane_reg.slot_id) + + + # all_lane_reg_values = reg_tool.read_opt_regs(exp_id, [2], [3], ['mgc']) + # for lane_reg in all_lane_reg_values: + # if lane_reg.mgc != -1: + # new_mgc = lane_reg.mgc + ext_value + # reg_tool.write_mgc_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, new_mgc) + # reg_tool.write_confirm_reg(exp_id, lane_reg.slot_id) + + # all_lane_reg_values = reg_tool.read_opt_regs(exp_id, [0,1], [2], ['mgc']) + # for lane_reg in all_lane_reg_values: + # if lane_reg.mgc != -1: + # new_mgc = lane_reg.mgc + ext_value + # reg_tool.write_mgc_reg(exp_id, lane_reg.slot_id, lane_reg.logic_lane, new_mgc) + # reg_tool.write_confirm_reg(exp_id, lane_reg.slot_id) + + +def SwaAutoTune(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + + for slot_id in slot_list: + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xD0, 0x8A, 1, "01") + for lane_id in lane_list: + max_index, max_value, min_index, min_value = get_swa_adc_per_lane(local_bmc, exp_id, slot_id, lane_id) + if max_value != 0 and min_value != 0: + # swa: max -> onoc min-> onet, swb: max -> onetb + time.sleep(0.05) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swa_onoc_h') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swa_onoc_m') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swa_onoc_l') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swa_onet_h') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swa_onet_m') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swa_onet_l') + time.sleep(0.08) + else: + logging.error(f'Read swa pic failed') + +def SwbAutoTune(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + + for slot_id in slot_list: + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xD0, 0x8A, 1, "01") + for lane_id in lane_list: + max_index, max_value, min_index, min_value = get_swb_adc_per_lane(local_bmc, exp_id, slot_id, lane_id) + if max_value != 0 and min_value != 0: + # swa: max -> onoc min-> onet, swb: max -> onetb + time.sleep(0.05) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swb_onetb_h') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swb_onetb_m') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, max_index, 'swb_onetb_l') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swb_oneta_h') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swb_oneta_m') + time.sleep(0.08) + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, min_index, 'swb_oneta_l') + time.sleep(0.08) + else: + logging.error(f'Read swb pic failed') + +def get_swa_adc_per_lane(local_bmc: DevWhiteRiverExp, exp_id: int, slot_id: int, lane_id: int): + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAB, 1, "02") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAC, 1, f"{lane_id:02x}") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAD, 1, f"{lane_id:02x}") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAE, 1, f"01") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAF, 1, f"01") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAA, 1, "07") + time.sleep(0.05) + logging.info('step 1') + + time.sleep(3) + status = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xB0, 1) + if status == 'aa': + + swa_adc1 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA0, 0x80, 128) + swa_adc2 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA1, 0x80, 128) + swa_adc3 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA2, 0x80, 128) + swa_adc4 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA3, 0x80, 128) + swa_adc5 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA4, 0x80, 128) + swa_adc6 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA5, 0x80, 128) + swa_adc7 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA6, 0x80, 128) + swa_adc8 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA7, 0x80, 128) + + full_hex_string = swa_adc1 + swa_adc2 + swa_adc3 + swa_adc4 + swa_adc5 + swa_adc6 + swa_adc7 + swa_adc8 + + byte_strings = [full_hex_string[i:i+4] for i in range(0, len(full_hex_string), 4)] + + data_values = [int(byte_str, 16) for byte_str in byte_strings] + + max_value = max(data_values) + min_value = min(data_values) + max_index = data_values.index(max_value) + min_index = data_values.index(min_value) + + x = list(range(len(data_values))) + plt.figure(figsize=(12, 6)) + plt.plot(x, data_values, linewidth=1.5) + plt.title('Data Curve Plot', fontsize=16) + plt.xlabel('idac', fontsize=12) + plt.ylabel('mpd', fontsize=12) + plt.grid(True, alpha=0.3) + plt.tight_layout() + + plt.savefig('data_curve.png', dpi=300, bbox_inches='tight') + plt.close() + + # logging.info(f"full_hex_string: {full_hex_string}") + # logging.info(f'data_values: {data_values}') + logging.info(f"max value: {max_value}, index: {max_index}") + logging.info(f"min value: {min_value}, index: {min_index}") + + return (max_index, max_value, min_index, min_value) + else: + return (0, 0, 0, 0) + +def get_swb_adc_per_lane(local_bmc: DevWhiteRiverExp, exp_id: int, slot_id: int, lane_id: int): + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAB, 1, "03") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAC, 1, f"{lane_id:02x}") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAD, 1, f"{lane_id:02x}") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAE, 1, f"01") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAF, 1, f"01") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAA, 1, "07") + time.sleep(0.05) + logging.info('step 1') + + time.sleep(3) + status = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xB0, 1) + if status == 'aa': + + swb_adc1 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA0, 0x80, 128) + swb_adc2 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA1, 0x80, 128) + swb_adc3 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA2, 0x80, 128) + swb_adc4 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA3, 0x80, 128) + swb_adc5 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA4, 0x80, 128) + swb_adc6 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA5, 0x80, 128) + swb_adc7 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA6, 0x80, 128) + swb_adc8 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA7, 0x80, 128) + + full_hex_string = swb_adc1 + swb_adc2 + swb_adc3 + swb_adc4 + swb_adc5 + swb_adc6 + swb_adc7 + swb_adc8 + + byte_strings = [full_hex_string[i:i+4] for i in range(0, len(full_hex_string), 4)] + + data_values = [int(byte_str, 16) for byte_str in byte_strings] + + max_value = max(data_values) + min_value = min(data_values) + max_index = data_values.index(max_value) + min_index = data_values.index(min_value) + + x = list(range(len(data_values))) + plt.figure(figsize=(12, 6)) + plt.plot(x, data_values, linewidth=1.5) + plt.title('Data Curve Plot', fontsize=16) + plt.xlabel('idac', fontsize=12) + plt.ylabel('mpd', fontsize=12) + plt.grid(True, alpha=0.3) + plt.tight_layout() + + plt.savefig(f'main_data/data_curve/data_curve_{lane_id}.png', dpi=300, bbox_inches='tight') + plt.close() + + # logging.info(f"full_hex_string: {full_hex_string}") + # logging.info(f'data_values: {data_values}') + logging.info(f"max value: {max_value}, index: {max_index}") + logging.info(f"min value: {min_value}, index: {min_index}") + + return (max_index, max_value, min_index, min_value) + else: + return (0, 0, 0, 0) + +def find_rising_edge_47_percent(y_data): + if not y_data or len(y_data) < 2: + return None + + y_min = min(y_data) + y_max = max(y_data) + if y_max == y_min: + return None # 无变化 + + target_y = y_min + 0.47 * (y_max - y_min) + + # 从左到右扫描上升沿 + for i in range(len(y_data) - 1): + y_curr = y_data[i] + y_next = y_data[i + 1] + + # 必须是上升趋势 + if y_next > y_curr and y_curr <= target_y < y_next: + # 线性插值 + x_curr = i * 8 + x_next = (i + 1) * 8 + # 避免除零(虽然 y_next > y_curr 已保证) + slope = (y_next - y_curr) / (x_next - x_curr) + x_target = x_curr + (target_y - y_curr) / slope + return x_target + + # 如果没找到,可能目标值在最后一点之后?再检查是否等于最后一个点 + if abs(y_data[-1] - target_y) < 1e-6: + return (len(y_data) - 1) * 8 + + # 未找到上升沿穿越点 + return None + +def MzmAutoTune(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + + for slot_id in slot_list: + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xD0, 0x8A, 1, "01") + for lane_id in lane_list: + + best_mpd = 0 + best_idac = 0 + for vadc in range(0, 4096, 800): + local_reg_tool.write_opt_reg(exp_id, slot_id, lane_id, vadc, 'vdac_runtime') + time.sleep(0.05) + + max_index, max_value, min_index, min_value = get_swa_adc_per_lane(local_bmc, exp_id, slot_id, lane_id) + if max_value > best_mpd: + best_mpd = max_value + best_idac = max_index + # set idac + logging.info(f"vadc: {vadc}, current_value: {max_value}, best: {best_idac}") + + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAB, 1, "02") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAC, 1, f"{lane_id:02x}") + time.sleep(0.05) + idac_str = best_idac.to_bytes(2, 'big').hex() + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAD, 2, idac_str) + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAF, 1, f"00") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAA, 1, "02") + time.sleep(0.05) + + time.sleep(0.5) + status = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xB0, 1) + logging.info(f'slot:{slot_id}, lane:{lane_id}, write max idac: {idac_str}, return status: {status}') + + # 细扫 + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAB, 1, f"{lane_id:02x}") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAC, 1, f"08") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAD, 1, f"02") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAE, 1, f"01") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAF, 1, f"00") + time.sleep(0.05) + local_bmc.SetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xAA, 1, "0B") + time.sleep(0.05) + logging.info('step 1') + + time.sleep(3) + status = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xB3, 0xB0, 1) + if status == 'aa': + + swa_adc1 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA0, 0x80, 128) + swa_adc2 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA1, 0x80, 128) + swa_adc3 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA2, 0x80, 128) + swa_adc4 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA3, 0x80, 128) + swa_adc5 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA4, 0x80, 128) + swa_adc6 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA5, 0x80, 128) + swa_adc7 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA6, 0x80, 128) + swa_adc8 = local_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0x00, 0xA7, 0x80, 128) + + full_hex_string = swa_adc1 + swa_adc2 + swa_adc3 + swa_adc4 + swa_adc5 + swa_adc6 + swa_adc7 + swa_adc8 + + byte_strings = [full_hex_string[i:i+4] for i in range(0, len(full_hex_string), 4)] + + data_values = [int(byte_str, 16) for byte_str in byte_strings] + + max_value = max(data_values) + min_value = min(data_values) + max_index = data_values.index(max_value) + min_index = data_values.index(min_value) + + x = [i * 8 for i in range(len(data_values))] + plt.figure(figsize=(12, 6)) + plt.plot(x, data_values, linewidth=1.5) + plt.title('Data Curve Plot', fontsize=16) + plt.xlabel('vdac', fontsize=12) + plt.ylabel('mpd', fontsize=12) + plt.grid(True, alpha=0.3) + plt.tight_layout() + + plt.savefig(f'mzm_vdac_{lane_id}.png', dpi=300, bbox_inches='tight') + plt.close() + + logging.info(f"full_hex_string: {full_hex_string}") + logging.info(f'data_values: {data_values}') + logging.info(f"max value: {max_value}, index: {max_index}") + logging.info(f"min value: {min_value}, index: {min_index}") + + x_at_47 = find_rising_edge_47_percent(data_values) + raw_vdac = local_reg_tool.read_opt_reg(exp_id, slot_id, lane_id, 'vdac') + logging.info(f'------------- raw_vdac: {raw_vdac}, new vdac: {x_at_47}') + + else: + logging.info(f'-------- status: {status}') + +def EyeCheck(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + tool.eye_check(host, exp_id, route_name) + +def RecvErrCheck(host: str, remote_host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, retimers: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, is_only_recv = False, is_show_all = False): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + eq_tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + + tool = RecvCeTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, eq_tool, ibias_rssi_map, topo_map, route_name) + tool.recv_err_check(host, exp_id, route_name, retimers, is_only_recv, is_show_all) + if remote_host != '' and remote_host != host: + time.sleep(0.05) + remote_tool = RecvCeTool(remote_bmc, local_bmc, remote_reg_tool, local_reg_tool, eq_tool, ibias_rssi_map, topo_map, route_name) + remote_tool.recv_err_check(remote_host, exp_id, route_name, retimers, is_only_recv, is_show_all) + +def CalcCtleMap(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + if param_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in param_list] + + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + # tool.eq_auto_tune(exp_id) + if param == '': + offset = 0 + else: + offset = int(param) + # tool.calc_ctle_value_by_pcb_trace_tx_rx_decoup(offset) + tool.calc_ctle_value_by_pcb_trace_pre6_post0(offset) + +def AutoTuneEq(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + if param_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in param_list] + + + logging.info(f"start auto tune eq: exp_id={exp_id}, slots={slot_list}") + tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + # tool.eq_auto_tune(exp_id) + + # tool.tune_eq_by_rtmr_length(exp_id, [1,2,3,4], False) + tool.tune_eq_by_eye_width(exp_id, retimers) + # tool.tune_eq_by_eye_width_ctle(exp_id, retimers) + +def TuneView(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, + rtmr_id: int, rtmr_lane: int): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start manual tune view: exp_id={exp_id}, slots={slot_list}") + tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + logging.info(f'------------- step 1') + tool.tune_view(exp_id, rtmr_id, rtmr_lane) + +def TuneWrite(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, lane_list: List[int], + remote_bmc: DevWhiteRiverExp, route_name: str, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, + rtmr_id: int, rtmr_lane: int, reg_name: str, reg_value: int): + + logging.info(f'lane_list: {lane_list}') + slot_list = [0,1,2,3,4,5,6,7] + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + + logging.info(f"start manual tune write: exp_id={exp_id}, slots={slot_list}") + tool = EqTuneTool(local_bmc, remote_bmc, local_reg_tool, remote_reg_tool, ibias_rssi_map, topo_map, route_name) + logging.info(f'------------- step 1') + tool.tune_write(exp_id, rtmr_id, rtmr_lane, reg_name, reg_value) + +def SetPower(host: str, exp: int, local_bmc: DevWhiteRiverExp, param: str): + if param == '950': + logging.info(f'----host: {host} exp: {exp}, set power 950') + local_bmc.CmdVendorCommand(exp, "exp pause") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0 0") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x24 0x32 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0xDB 0x2E") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x21 0x2E 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x11") + local_bmc.CmdVendorCommand(exp, "exp resume") + local_bmc.CmdVendorCommand(exp, "exp") + elif param == '930': + logging.info(f'----host: {host} exp: {exp}, set power 930') + local_bmc.CmdVendorCommand(exp, "exp pause") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0 0") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x24 0x32 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0xDB 0x2C") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x21 0x2C 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x11") + local_bmc.CmdVendorCommand(exp, "exp resume") + local_bmc.CmdVendorCommand(exp, "exp") + elif param == '910': + logging.info(f'----host: {host} exp: {exp}, set power 910') + local_bmc.CmdVendorCommand(exp, "exp pause") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0 0") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x24 0x2D 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0xDB 0x2A") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x21 0x2A 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x11") + local_bmc.CmdVendorCommand(exp, "exp resume") + local_bmc.CmdVendorCommand(exp, "exp") + elif param == '950': + logging.info(f'----host: {host} exp: {exp}, set power 950') + local_bmc.CmdVendorCommand(exp, "exp pause") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0 0") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x24 0x32 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0xDB 0x2E") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x21 0x2E 0x00") + local_bmc.CmdVendorCommand(exp, "iicm i2c@40008400 0x61 w 0x11") + local_bmc.CmdVendorCommand(exp, "exp resume") + local_bmc.CmdVendorCommand(exp, "exp") + +TCT_SN_FILE = "main_data/tct_sn.txt" + +# 全局缓存,避免重复读取文件 +_tct_sn_set = None + +def load_tct_sn_from_file(): + """只加载一次 tct_sn.txt,返回包含所有已温循 SN 的集合""" + global _tct_sn_set + if _tct_sn_set is not None: + return _tct_sn_set + + _tct_sn_set = set() + + if not os.path.exists(TCT_SN_FILE): + logging.error(f"[TCT] 文件不存在: {TCT_SN_FILE}") + return _tct_sn_set + + try: + with open(TCT_SN_FILE, 'r', encoding='utf-8') as f: + for line_num, line in enumerate(f, 1): + stripped = line.strip() + # 跳过空行和注释行 + if not stripped or stripped.startswith('#'): + continue + _tct_sn_set.add(stripped) + logging.info(f"[TCT] 已加载 {len(_tct_sn_set)} 个经过温循的 SN") + except Exception as e: + logging.error(f"[TCT] 读取 TCT SN 文件失败: {e}") + + return _tct_sn_set + +def check_module_tct_status(host: str, bmc, exp_id: int, slot_list): + global _tct_sn_set + + # 步骤1:加载 TCT SN 列表 + if _tct_sn_set is None: + _tct_sn_set = load_tct_sn_from_file() + for slot_id in slot_list: + + # 步骤2:从 BMC 读取模块 SN (字节 166 开始,共 16 字节) + try: + res_data = bmc.GetOpticalModuleRegs(exp_id, slot_id, 0, 0, 166, 16) + if not res_data: + raise ValueError("BMC 返回空数据") + + bytes_data = bytes.fromhex(res_data) + # 解码为 ASCII,忽略非法字符,并去除首尾空白和填充字符(如 \x00, 空格) + raw_sn = bytes_data.decode('ascii', errors='ignore').strip('\x00 \t\n\r') + sn = raw_sn.strip() # 最终 SN + + if not sn: + sn = "Unknown_SN" + except Exception as e: + logging.error(f"[BMC] 读取 host:{host}, exp:{exp_id}, slot:{slot_id} SN 失败: {e}") + sn = "Read_Error" + + # 步骤3:判断是否经过温循 + is_tct_passed = sn in _tct_sn_set + + # 步骤4:统一格式化输出 + status_str = "Yes" if is_tct_passed else "No" + logging.info( + f"host: {host}, " + f"exp: {exp_id}, " + f"slot: {slot_id}, " + f"sn: {sn}, " + f"tct_passed: {status_str}" + ) + +def run_remote_cmd(host, cmd_str, password='RCms@Zte3'): + full_cmd = [ + 'sshpass', '-p', password, + 'ssh', '-o', 'StrictHostKeyChecking=no', + f'root@{host}', + cmd_str + ] + + logging.info(f"[RUN] {host}: {cmd_str}") + + # 启动进程,实时捕获输出 + proc = subprocess.Popen( + full_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, # 合并 stderr 到 stdout + universal_newlines=True, # 返回字符串而非 bytes + bufsize=1 # 行缓冲 + ) + + # 实时读取输出并打印 + for line in iter(proc.stdout.readline, ''): + if line: + logging.info(f"[{host}] {line.rstrip()}") + + proc.stdout.close() + return_code = proc.wait() + + if return_code != 0: + logging.error(f"[ERROR] Command failed on {host} (exit code: {return_code})", file=sys.stderr) + else: + logging.info(f"[OK] Command succeeded on {host}") + + return return_code + +def gpu_cto_disable(os_host_list, password='RCms@Zte3', container='mysccl-zds'): + logging.info(">>> GPU reset on hosts: %s", os_host_list) + base_cmd_list = [ + '/workspace/zds/gpu_debugger -i all -w 0x369098 0x00000010', + '/workspace/zds/gpu_debugger -i all -w 0x379098 0x00000010', + '/workspace/zds/gpu_debugger -i all -w 0x781098 0x00000010', + '/workspace/zds/gpu_debugger -i all -w 0x771098 0x00000010', + '/workspace/zds/gpu_debugger -i all -r 0x369098', + '/workspace/zds/gpu_debugger -i all -r 0x379098', + '/workspace/zds/gpu_debugger -i all -r 0x781098', + '/workspace/zds/gpu_debugger -i all -r 0x771098' + ] + + def exec_on_host(host): + for base_cmd in base_cmd_list: + cmd = f"docker exec {container} {base_cmd}" + run_remote_cmd(host, cmd, password) + + with concurrent.futures.ThreadPoolExecutor(max_workers=len(os_host_list)) as executor: + future_to_host = {executor.submit(exec_on_host, host): host for host in os_host_list} + for future in concurrent.futures.as_completed(future_to_host): + host = future_to_host[future] + try: + future.result() + except Exception as exc: + logging.error("Host %s exception: %s", host, exc) + +def gpu_reset(os_host_list, password='RCms@Zte3', container='mysccl-zds'): + base_cmd = "brsmi reset -g" + logging.info(">>> GPU reset on hosts: %s", os_host_list) + + def reset_on_host(host): + cmd = f"docker exec {container} {base_cmd}" + run_remote_cmd(host, cmd, password) + + with concurrent.futures.ThreadPoolExecutor(max_workers=len(os_host_list)) as executor: + future_to_host = {executor.submit(reset_on_host, host): host for host in os_host_list} + for future in concurrent.futures.as_completed(future_to_host): + host = future_to_host[future] + try: + future.result() + except Exception as exc: + logging.error("Host %s exception: %s", host, exc) + +def process_exp_cold_reset(host): + logging.info(f'-----------process: {host}, exp_list: {exp_list}') + for exp_id in exp_list: + exp_id = int(exp_id) + base_url = f"https://{host}" + # logging.info(f'-------step 0, base_url: {base_url}') + exp_util = SmbusHttpUtil(base_url, "root", "0penBmc", "test") + # logging.info(f'-------step 1') + # exp_util.lock(exp_id) + + vuart_util = VuartUtil(host) + bmc = DevWhiteRiverExp(exp_util, vuart_util) + + logging.info(f'start exec reset: {exp_id}') + ExpColdReset(bmc, exp_id) + logging.info(f'finish exec reset: {exp_id}') + + time.sleep(5) + + sec = bmc.GetExpStatus(exp_id) + logging.info(f'------update:{sec}') + if sec is not None: + while sec > 8: + logging.error(f'host {host} exp {exp_id} reset failed, try again') + ExpColdReset(bmc, exp_id) + time.sleep(5) + + sec = bmc.GetExpStatus(exp_id) + logging.info(f'------update:{sec}') + + logging.info(f'{host} done') + + +def process_rtmr_waram_reset(host): + logging.info(f'-----------process: {host}, exp_list: {exp_list}') + for exp_id in exp_list: + exp_id = int(exp_id) + base_url = f"https://{host}" + # logging.info(f'-------step 0, base_url: {base_url}') + exp_util = SmbusHttpUtil(base_url, "root", "0penBmc", "test") + # logging.info(f'-------step 1') + # exp_util.lock(exp_id) + + vuart_util = VuartUtil(host) + bmc = DevWhiteRiverExp(exp_util, vuart_util) + + logging.info(f'start exec retimer reset: {exp_id}') + rtmr_reset_and_port_reset(bmc, exp_id) + + + logging.info(f'{host} done') + +def rtmr_reset_and_port_reset(bmc: DevWhiteRiverExp, exp_id): + + bmc.CmdVendorCommand(exp_id, 'cmis mode lp') + time.sleep(0.15) + bmc.CmdVendorCommand(exp_id, 'rtmr 1 reset') + bmc.CmdVendorCommand(exp_id, 'rtmr 2 reset') + bmc.CmdVendorCommand(exp_id, 'rtmr 3 reset') + bmc.CmdVendorCommand(exp_id, 'rtmr 4 reset') + time.sleep(0.5) # 等待load成功 + + bmc.SetRetimerRegs(exp_id, 1, 0x0414, '11') + bmc.SetRetimerRegs(exp_id, 2, 0x0414, '11') + bmc.SetRetimerRegs(exp_id, 3, 0x0414, '11') + bmc.SetRetimerRegs(exp_id, 4, 0x0414, '11') + + time.sleep(1) + + bmc.CmdVendorCommand(exp_id, 'cmis mode hp') + + time.sleep(2) + + bmc.SetRetimerRegs(exp_id, 1, 0x0414, '00') + bmc.SetRetimerRegs(exp_id, 2, 0x0414, '00') + bmc.SetRetimerRegs(exp_id, 3, 0x0414, '00') + bmc.SetRetimerRegs(exp_id, 4, 0x0414, '00') + + logging.info(f'finish exec retimer reset: {exp_id}') + +def load_ctle_config(host: str, route_name: str, exp_id: int) -> List[RetimerLaneSetting]: + config_path = os.path.join("/xz/gyou/nexusbench/main_data", "ctle", f"{host}.json") + + if not os.path.exists(config_path): + logging.info(f"CTLE config file not found: {config_path}") + return [] + + try: + with open(config_path, 'r', encoding='utf-8') as f: + full_config = json.load(f) + + # 获取当前路由模式下的子配置 + mode_config = full_config.get(route_name) + if mode_config is None: + logging.info( + f"No CTLE configuration found for routing mode '{route_name}' in {config_path}" + ) + return [] + + exps = mode_config.get("exps", {}) + exp_key = str(exp_id) + if exp_key not in exps: + logging.info( + f"No CTLE config found for exp_id={exp_id} under route='{route_name}' in {config_path}" + ) + return [] + + retimers = exps[exp_key].get("retimers", {}) + results = [] + + for rtmr_id_str, rtmr_data in retimers.items(): + lanes = rtmr_data.get("lanes", {}) + for lane_str, lane_data in lanes.items(): + try: + ctle_val = lane_data.get("ctle") + if ctle_val is None: + continue + setting = RetimerLaneSetting( + rtmr_id=int(rtmr_id_str), + rtmr_lane=int(lane_str), + ctle=int(ctle_val) + ) + results.append(setting) + except (ValueError, TypeError) as e: + logging.warning( + f"Invalid CTLE entry in {config_path} [route={route_name}, exp={exp_id}]: " + f"retimer={rtmr_id_str}, lane={lane_str}, data={lane_data} ({e})" + ) + continue + + logging.info( + f"Loaded {len(results)} CTLE settings for host={host}, route={route_name}, exp={exp_id}" + ) + return results + + except json.JSONDecodeError as e: + logging.error(f"Failed to parse CTLE config file {config_path}: {e}") + return [] + except (OSError, IOError) as e: + logging.error(f"Failed to read CTLE config file {config_path}: {e}") + return [] + except Exception as e: + logging.error(f"Unexpected error loading CTLE config: {e}") + return [] + +def load_ctle_for_single_host_exp(host: str, port_list: List[int], need_lock = True): + for exp_id in port_list: + try: + exp_id = int(exp_id) + exp_util = SmbusHttpUtil(f"https://{host}", "root", "0penBmc", host) + vuart_util = VuartUtil(host) + bmc = DevWhiteRiverExp(exp_util, vuart_util) + + if need_lock: + exp_util.lock(exp_id) + logging.info(f'----------process_host {host} exp {exp_id} locked') + + time.sleep(0.05) + route_name = get_route_name(host, bmc, exp_id) + all_rtmr_lane_settings: List[RetimerLaneSetting] = load_ctle_config(host, route_name, exp_id) + + for lane_setting in all_rtmr_lane_settings: + WriteRtmrCtle(host, exp_id, bmc, [lane_setting.rtmr_id], [lane_setting.rtmr_lane], lane_setting.ctle) + time.sleep(0.05) + + if all_rtmr_lane_settings: + CommitRtmrCtle(exp_id, bmc, [1, 2, 3, 4]) + + time.sleep(0.05) + logging.info(f"✅ Completed {host} exp {exp_id}") + + except Exception as e: + logging.error(f"❌ Failed to process {host} exp {exp_id}: {e}") + raise # 可选:是否让主线程感知失败 + finally: + # 确保解锁(即使出错) + try: + if need_lock: + exp_util.unlock(exp_id) + logging.info(f'----------process_host {host} exp {exp_id} unlocked') + except Exception as unlock_err: + logging.warning(f"Failed to unlock {host} exp {exp_id}: {unlock_err}") + + +def link_train(host_list, os_host_list, port_list, topo_file_name, password='RCms@Zte3', container='mysccl-zds'): + base_cmd = "/workspace/zds" + script_disable = f"{base_cmd}/scripts/disable_port_ltssm" + topo_tool = f"{base_cmd}/ocsTopo_s" + + # === Step 1: Disable LTSSM for all (host, port) pairs === + logging.info(">>> Step 1: Disabling LTSSM on all hosts and ports...") + for port in port_list: + for host in os_host_list: + cmd = f"docker exec {container} {script_disable} {port}" + run_remote_cmd(host, cmd, password) + + # # === Step 2: ocs link reset (host, port) pairs === + logging.info(">>> Step 2: ocs link reset on all hosts and ports...") + + with ThreadPoolExecutor(max_workers=len(host_list)) as executor: + executor.map(process_exp_cold_reset, host_list) + # + time.sleep(6) + + # === Step 3: Load topology on all hosts === + logging.info("\n>>> Step 2: Loading topology on all hosts...") + topo_path = f"../../{base_cmd}/{topo_file_name}" + for host in os_host_list: + cmd = f"docker exec {container} {topo_tool} -s {topo_path}" + run_remote_cmd(host, cmd, password) + + # === Step 4: Load ctle values to retimer + logging.info("\n>>> Step 4: Loading ctle values to retimer...") + + if not host_list or not port_list: + logging.info("No hosts or ports to process.") + return + + max_workers = min(4, len(host_list)) + logging.info(f"Starting parallel processing across {len(host_list)} hosts with max_workers={max_workers}") + + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + future_to_host = { + executor.submit(load_ctle_for_single_host_exp, host, port_list): host + for host in host_list + } + for future in concurrent.futures.as_completed(future_to_host): + host = future_to_host[future] + try: + future.result() + except Exception as exc: + logging.error(f"Unexpected error in host {host} task: {exc}") + # === Step 5: Start link training (-p) on all hosts === + logging.info("\n>>> Step 3: Starting link training on all hosts...") + def run_on_host(host, port): + """在指定 host 上执行指定 port 的命令""" + cmd = f"docker exec {container} {topo_tool} -p {port}" + return run_remote_cmd(host, cmd, password) # 假设该函数已定义 + + # 外层:按 port 串行 + for port in port_list: + logging.info(">>> Starting link training for port %s on all hosts...", port) + + # 内层:当前 port 下,所有 host 并行 + with concurrent.futures.ThreadPoolExecutor(max_workers=len(os_host_list)) as executor: + # 提交任务:每个 host 执行当前 port + future_to_host = { + executor.submit(run_on_host, host, port): host + for host in os_host_list + } + + # 等待当前 port 的所有 host 完成 + for future in concurrent.futures.as_completed(future_to_host): + host = future_to_host[future] + try: + future.result() # 触发异常(如有) + except Exception as exc: + logging.error("Host %s failed for port %s: %s", host, port, exc) + + logging.info(">>> Completed port %s on all hosts.", port) + +ENABLE_ADDR_MAP = { + 0: 0x127000, 1: 0x127400, 2: 0x127800, 3: 0x127C00, + 4: 0x137000, 5: 0x137400, 6: 0x137800, 7: 0x137C00, + 8: 0x147000, 9: 0x147400, 10: 0x147800, 11: 0x147C00, + 12: 0x157000, 13: 0x157400, 14: 0x157800, 15: 0x157C00 +} + +REG_ADDR_MAP = { + 0: 0x127004, 1: 0x127404, 2: 0x127804, 3: 0x127C04, + 4: 0x137004, 5: 0x137404, 6: 0x137804, 7: 0x137C04, + 8: 0x147004, 9: 0x147404, 10: 0x147804, 11: 0x147C04, + 12: 0x157004, 13: 0x157404, 14: 0x157804, 15: 0x157C04 +} + +def WriteRtmrCtle(host: str, exp_id: int, bmc: DevWhiteRiverExp, rtmrs: List[int], rtmr_lanes: List[int], ctle_value: int): + enable_addr_value = 0xabcdabcd + enable_addr_value_str = enable_addr_value.to_bytes(4, 'little').hex() + ctle_value_str = ctle_value.to_bytes(4, 'little').hex() + for rtmr in rtmrs: + for rtmr_lane in rtmr_lanes: + logging.info(f'{host} WriteRtmrCtle exp:{exp_id}, rtmrs:{rtmr}, lane: {rtmr_lane}, addr: {ENABLE_ADDR_MAP[rtmr_lane]}, ctle: {ctle_value}') + bmc.SetRetimerRegs(exp_id, rtmr, ENABLE_ADDR_MAP[rtmr_lane], enable_addr_value_str) + time.sleep(0.05) + bmc.SetRetimerRegs(exp_id, rtmr, REG_ADDR_MAP[rtmr_lane], ctle_value_str) + + # value = 0x100 + # value_str = value.to_bytes(4, 'little').hex() + # bmc.SetRetimerRegs(exp_id, rtmr, 0x414, value_str) + # time.sleep(0.5) + # value = 0x0 + # value_str = value.to_bytes(4, 'little').hex() + # bmc.SetRetimerRegs(exp_id, rtmr, 0x414, value_str) + +def CommitRtmrCtle(exp_id: int, bmc: DevWhiteRiverExp, rtmrs: List[int]): + logging.info(f'Commit Rtmr {rtmrs} Ctle') + for rtmr in rtmrs: + value = 0x100 + value_str = value.to_bytes(4, 'little').hex() + bmc.SetRetimerRegs(exp_id, rtmr, 0x414, value_str) + time.sleep(1) + value = 0x0 + value_str = value.to_bytes(4, 'little').hex() + bmc.SetRetimerRegs(exp_id, rtmr, 0x414, value_str) + +def get_route_name(host, bmc, exp_id) -> str: + + route_info = bmc.GetOpticalRouteStatus(exp_id, 0) + # logging.info(f'--------GetOpticalRouteStatus route_info:{route_info}') + yaml_route_map = { + '1111111111111111': '786-1-oneta', + '1212121212121212': '786-1-onetb', + '0201040306050807': '786-1-onoc1', + '0403020108070605': '786-1-onoc2', + '0807060504030201': '786-1-onoc3', + '0304010207080506': '786-1-onoc4', + '0605080702010403': '786-1-onoc5', + '0708050603040102': '786-1-onoc6', + '0506070801020304': '786-1-onoc7', + } + route_name = yaml_route_map[route_info] + return route_name + +raw_data_91 = { + 0: [319, 329, 314, 326, 306, 332, 318, 320], + 1: [317, 338, 320, 349, 320, 328, 318, 355], + 2: [322, 336, 325, 341, 318, 325, 313, 316], + 3: [315, 323, 316, 338, 304, 311, 302, 318], + 4: [301, 315, 314, 327, 314, 317, 301, 316], + 5: [303, 306, 300, 317, 308, 330, 306, 318], + 6: [308, 320, 308, 326, 313, 340, 314, 295], + 7: [305, 324, 313, 324, 307, 312, 288, 322], +} + +raw_data_94 = { + 0: [307,316,303,325,320,335,321,326], + 1: [316,333,320,345,308,338,310,324], + 2: [347,334,344,322,311,333,328,337], + 3: [316,339,321,337,322,337,321,332], + 4: [286,307,308,313,301,323,314,321], + 5: [222,234,216,236,222,244,226,234], + 6: [332,325,344,344,330,339,329,353], + 7: [296,305,295,322,302,320,294,315] +} + +def print_general_err_matrix(host, exp, lane_data_list: List[LaneErrInfo], tune_value_list, excel_path="err_matrix.xlsx"): + tune_value_list = sorted(tune_value_list) + + header_parts = ["Host", "Exp", "Preset", "Slot", "Lane", "RT_Id", "RT_Lane"] + [str(v) for v in tune_value_list] + + header_format = "{:<16} {:<6} {:<8} {:<6} {:<6} {:<6} {:<10} " + " ".join("{:<6}" for _ in tune_value_list) + logging.info(header_format.format(*header_parts)) + logging.info("-" * (len(header_format.format(*header_parts)))) + + rows = [] + + for info in lane_data_list: + try: + err_counts = [] + for value in tune_value_list: + err = info.tmp_err_map.get(value, -2) + if err < 0: + err_str = "70000" + else: + err_str = str(err) + err_counts.append(err_str) + + row_data = [ + host, + exp, + info.preset, + info.slot_id, + info.logic_lane, + info.rt_phys_id, + info.rt_phys_lane, + ] + err_counts + + data_format = "{:<16} {:<6} {:<8} {:<6} {:<6} {:<6} {:<10} " + " ".join("{:<6}" for _ in tune_value_list) + logging.info(data_format.format(*row_data)) + + row_dict = dict(zip(header_parts, row_data)) + row_dict["Timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + rows.append(row_dict) + + except Exception as e: + logging.error(f"Error processing row for Slot {info.slot_id}, Lane {info.logic_lane}: {e}") + continue + + # === 写入 Excel(增量追加)=== + try: + # ✅ 修复点:read_excel 也要指定 engine='openpyxl' + try: + existing_df = pd.read_excel(excel_path, engine='openpyxl') + except FileNotFoundError: + existing_df = pd.DataFrame() + + new_df = pd.DataFrame(rows) + updated_df = pd.concat([existing_df, new_df], ignore_index=True) + + # ✅ 写入时也指定 engine(虽非必须,但更安全) + updated_df.to_excel(excel_path, index=False, engine='openpyxl') + logging.info(f"✅ Data appended to {excel_path}") + + except Exception as e: + logging.error(f"Failed to write Excel file: {e}") + +def load_inmpd_data(file_path="main_data/vdac/inmpd.xlsx"): + logging.info(f'load_inmpd_data') + if not os.path.exists(file_path): + raise FileNotFoundError(f"Excel file not found: {file_path}") + try: + logging.info(f"Opening Excel file: {file_path}") + xls = pd.ExcelFile(file_path, engine='openpyxl') + logging.info(f"Excel file opened successfully. Sheets: {xls.sheet_names}") + except Exception as e: + raise ValueError(f"Failed to open Excel file '{file_path}': {e}") + + required_cols = ['sn', 'lane', 'inmpd', 'temp'] + all_data = [] + + logging.info(f'step 1.1') + for sheet_name in xls.sheet_names: + try: + logging.info(f'step 2') + df = pd.read_excel(xls, sheet_name=sheet_name) + + # 标准化列名:转小写并去除前后空格 + df.columns = df.columns.astype(str).str.strip().str.lower() + logging.info(f'step 3') + # 检查是否包含所有必需列 + if not all(col in df.columns for col in required_cols): + missing = set(required_cols) - set(df.columns) + print(f"⚠️ Sheet '{sheet_name}' skipped: missing columns {missing}") + continue + + # 提取所需列 + selected = df[required_cols].copy() + all_data.append(selected) + + except Exception as e: + print(f"❌ Error reading sheet '{sheet_name}': {e}") + + if not all_data: + raise ValueError("No valid sheet contains all required columns: sn, lane, inmpd, temp") + + # 合并所有有效数据 + combined_df = pd.concat(all_data, ignore_index=True) + return combined_df + +def inmpd_check(host: str, local_bmc: DevWhiteRiverExp, exp_id: int, slot_list: list, + local_reg_tool: OptRegAccessTool, output_dir: str = "tmp_data/inmpd"): + logging.info('step 1') + inmpd_df = load_inmpd_data() # DataFrame with columns: sn, lane, inmpd, temp + logging.info(f'Loaded {len(inmpd_df)} rows from Excel') + + results = [] # 🟢 用于收集所有结果 + + for slot in slot_list: + sn = local_reg_tool.read_sn(exp_id, slot) + temp = local_reg_tool.read_temp(exp_id, slot) + logging.info(f"Processing slot {slot}, SN: {sn}, TEMP: {temp}") + + matched_rows = inmpd_df[inmpd_df['sn'] == sn] + if matched_rows.empty: + logging.warning(f"No data found in Excel for SN: {sn}") + continue + + # Step 1: 保存原始 IBIAS 并设置为 4096 + ibias_lane_list = [0, 1, 2, 3] + raw_ibias_list = [] + + local_bmc.SetOpticalModuleRegs(exp_id, slot, 0, 0xB4, 0x88, 1, '01') + time.sleep(0.05) + for lane in ibias_lane_list: + # raw_ibias = local_reg_tool.read_ibias_reg_by_ibias_lane(exp_id, slot, lane) + # raw_ibias_list.append(raw_ibias) + # time.sleep(0.05) + local_reg_tool.write_vol_ibias_reg_by_ibias_lane(exp_id, slot, lane, 4096) + time.sleep(0.05) + + time.sleep(2) + + # Step 2: 读取 INMPD,并恢复 IBIAS + for lane in ibias_lane_list: + ibias = local_reg_tool.read_vol_ibias_reg_by_ibias_lane(exp_id, slot, lane) + pic_inmpd = local_reg_tool.read_pic_inmpd(exp_id, slot, lane) + try: + pic_inmpd_dbm = round(10 * math.log10(pic_inmpd / 2 * 41.88034 / 10000), 3) + except (ValueError, ZeroDivisionError): + pic_inmpd_dbm = float('nan') + + # time.sleep(0.05) + # local_reg_tool.write_ibias_reg_by_ibias_lane(exp_id, slot, lane, raw_ibias_list[lane]) + # time.sleep(0.05) + + cm_inmpd = -1 + cm_inmpd_dbm = float('nan') + cm_temp = -1 + + target_inmpd_lane = (lane + 1) * 2 + for _, row in matched_rows.iterrows(): + inmpd_lane = int(row['lane']) + if inmpd_lane == target_inmpd_lane: + cm_inmpd = int(row['inmpd']) + try: + cm_inmpd_dbm = round(10 * math.log10(cm_inmpd / 2 * 41.88034 / 10000), 3) + except (ValueError, ZeroDivisionError): + cm_inmpd_dbm = float('nan') + cm_temp = int(row['temp']) + break + + # 安全计算 ratio + if cm_inmpd == 0: + ratio = float('nan') + else: + ratio = (cm_inmpd - pic_inmpd) / cm_inmpd + + result_entry = { + 'host': host, + 'exp_id': exp_id, + 'sn': sn, + 'slot': slot, + 'lane': lane, + 'current_temp': temp, + 'cm_temp': cm_temp, + 'ibias': ibias, + 'current_inmpd': pic_inmpd, + 'cm_inmpd': cm_inmpd, + 'pic_inmpd_dbm': pic_inmpd_dbm, + 'cm_inmpd_dbm': cm_inmpd_dbm, + 'inmpd_ratio': ratio, + } + results.append(result_entry) + + # ✅ 统一打印所有结果 + for entry in results: + ratio_pct = entry['inmpd_ratio'] * 100 if not pd.isna(entry['inmpd_ratio']) else float('nan') + ratio_str = f"{ratio_pct:.2f}%" if not pd.isna(ratio_pct) else "N/A" + logging.info( + f"host: {entry['host']}, " + f"exp: {entry['exp_id']}, " + f"sn: {entry['sn']}, " + f"ibias: {entry['ibias']}, " + f"slot: {entry['slot']}, " + f"lane: {entry['lane']}, " + f"temp(current/cm): {entry['current_temp']}/{entry['cm_temp']}, " + f"inmpd(current/cm): {entry['current_inmpd']}/{entry['cm_inmpd']}, " + f"inmpd_dbm(current/cm): {entry['pic_inmpd_dbm']}/{entry['cm_inmpd_dbm']}, " + f"inmpd_ratio: {ratio_str}" + ) + + # ✅ 保存到 Excel + if results: + df_results = pd.DataFrame(results) + os.makedirs(output_dir, exist_ok=True) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"{output_dir}/inmpd_check_{host}_exp{exp_id}_{timestamp}.xlsx" + df_results.to_excel(filename, index=False) + logging.info(f"Results saved to: {filename}") + else: + logging.warning("No valid results to save.") + + return results + +def process_host(host, reg_table_file, exp_list, slot_list, rtmr_list, lane_list, cmd: str, param, reg_wt_value, sl_file): + + # logging.info(f'-----------process: {host}') + + ibias_rssi_map_file = '/xz/gyou/nexusbench/main_data/ibias-rssi-map.json' + if Path(ibias_rssi_map_file).exists() == False: + logging.error(f'ibias_rssi_map_file {ibias_rssi_map_file} not found!') + return + + ibias_rssi_map = IbiasRssiMapParser(ibias_rssi_map_file) + + for exp_id in exp_list: + exp_id = int(exp_id) + + exp_util = SmbusHttpUtil(f"https://{host}", "root", "0penBmc", host) + + exp_util.lock(exp_id) + logging.info(f'----------process_host {host} lock') + + vuart_util = VuartUtil(host) + bmc = DevWhiteRiverExp(exp_util, vuart_util) + + route_info = bmc.GetOpticalRouteStatus(exp_id, 0) + # logging.info(f'--------GetOpticalRouteStatus route_info:{route_info}') + yaml_route_map = { + '1111111111111111': '786-1-oneta', + '1212121212121212': '786-1-onetb', + '0201040306050807': '786-1-onoc1', + '0403020108070605': '786-1-onoc2', + '0807060504030201': '786-1-onoc3', + '0304010207080506': '786-1-onoc4', + '0605080702010403': '786-1-onoc5', + '0708050603040102': '786-1-onoc6', + '0506070801020304': '786-1-onoc7', + } + + reg_table_file = f'/xz/gyou/nexusbench/main_data/{yaml_route_map[route_info]}.yaml' + route_name = yaml_route_map[route_info] + # logging.info(f'input reg_table_file is empty, use default config file:{reg_table_file}, route_name:{route_name}') + + if Path(reg_table_file).exists(): + # print(f"File exists: {reg_table_file}") + reg_access_tool = OptRegAccessTool(host, reg_table_file, bmc) + logging.info(f'init reg_access_tool with reg_table_file:{reg_table_file}, route_name:{route_name}') + + remote_reg_access_tool = reg_access_tool + # else: + # print(f"File NOT found: {file_path}") + remote_bmc = None + remote_exp_util = exp_util + remote_host = host + + if cmd in ['ibias-auto-tune', 'plot-mgc-vpeak', 'eq-auto-tune', 'tune-rd', 'tune-wt', 'rssi-check', 'err-check', + 'rtmr-prbs', 'rtmr-prbs-test', 'ctle-auto-tune', 'mgc-auto-tune', 'mgc-auto-tune2', 'lowfreq-auto-tune', + 'highfreq-auto-tune', 'tiapeak-auto-tune', 'opcurrent-auto-tune', 'opcurrent-auto-tune-test', 'rt-ibias', 'rt-mgc'] and remote_ip != '': + logging.info(f'------- remote_host: {remote_ip}') + remote_host = remote_ip + remote_exp_util = SmbusHttpUtil(f"https://{remote_host}", "root", "0penBmc", remote_host) + remote_bmc = DevWhiteRiverExp(remote_exp_util, vuart_util) + remote_exp_util.lock(exp_id) + logging.info(f'----------process dst host {remote_host} lock') + + remote_reg_access_tool = OptRegAccessTool(remote_host, reg_table_file, remote_bmc) + + if cmd in ['eq-auto-tune', 'rtmr-prbs-test', 'tune-rd', 'tune-wt', 'mgc-auto-tune2', 'lowfreq-auto-tune', 'err-check', 'highfreq-tune', + 'highfreq-auto-tune', 'tiapeak-auto-tune', 'opcurrent-auto-tune', 'opcurrent-auto-tune-test', 'rt-ibias', 'rt-mgc']: + slot_list = [0,1,2,3,4,5,6,7] + + if cmd in ['reg-rd', 'inmpd-check', 'reg-rd-test', 'tx-enable', 'tx-disable', 'rtmr-prbs-test', 'swa-auto-tune', 'swb-auto-tune', 'mzm-auto-tune', 'opcurrent-tune', 'reg-wt', 'rssi-check', 'eye-check', 'err-check', \ + 'topo-config', 'plot-mgc-vpeak', 'mgc-auto-tune', 'mgc-auto-tune2', 'lowfreq-auto-tune', + 'highfreq-auto-tune', 'tiapeak-auto-tune', 'opcurrent-auto-tune', 'opcurrent-auto-tune-test', 'rt-ibias', 'rt-mgc', 'mgc-manual-tune', + 'mgc-calc-target-vpeak', 'ibias-auto-tune', 'reg-save', 'reg-load', + 'highfreq-tune', 'tia-peak-tune', 'eq-auto-tune', 'tune-rd', 'tune-wt']: + for slot_id in slot_list: + # read sn + logging.info('------------ Authentication 1') + Authentication(bmc, exp_id, slot_id) + # res_data = bmc.GetOpticalModuleRegs(exp_id, slot_id, 0, 0, 166, 16) + # logging.debug(res_data) + # bytes_data = bytes.fromhex(res_data) + # ascii_str = bytes_data.decode('ascii', errors='ignore') + # logging.info(f'----------------slot: {slot_id}, sn:{ascii_str}') + + if remote_bmc: + logging.info('------------ Authentication 2') + Authentication(remote_bmc, exp_id, slot_id) + # res_data = remote_bmc.GetOpticalModuleRegs(exp_id, slot_id, 0, 0, 166, 16) + # logging.debug(res_data) + # bytes_data = bytes.fromhex(res_data) + # ascii_str = bytes_data.decode('ascii', errors='ignore') + # logging.info(f'----------------Remote slot: {slot_id}, sn:{ascii_str}') + + # mod_type = ReadModuleType(bmc, exp_id, slot_id) + + # logging.info(f'Raw mod_type: {repr(mod_type)}') + # file_module_type = reg_access_tool.parser.get_module_type() + + # is_valid_combination = ( + # (mod_type.startswith('0.0.0') and file_module_type == 'batch0') or + # (mod_type.startswith('ISGD5680H') and file_module_type == 'batch1') or + # (mod_type.startswith('XZDRV') and file_module_type == 'batch2')) + + # if not is_valid_combination: + # logging.error(f'config file is not match!') + # sys.exit() + if cmd == 'read-sn': + logging.info(f'slot_list: {slot_list}') + check_module_tct_status(host, bmc, exp_id, slot_list) + # for slot_id in slot_list: + # res_data = bmc.GetOpticalModuleRegs(exp_id, slot_id, 0, 0, 166, 16) + # bytes_data = bytes.fromhex(res_data) + # ascii_str = bytes_data.decode('ascii', errors='ignore') + # logging.info(f'----------------host: {host}, exp: {exp_id}, slot: {slot_id}, sn:{ascii_str}') + elif cmd == 'reg-rd-test': + rd_regs = param_list + # all_lane_reg_values: List[LaneRegInfo] = reg_access_tool.read_opt_regs(exp_id, slot_list, lane_list, rd_regs) + + for slot in slot_list: + value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0xB0, size=14) + logging.info(f'00 CD B0: {value}') + value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0x90, size=14) + logging.info(f'00 CD 90: {value}') + + time.sleep(0.05) + bmc.SetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0xB0, size=14, hex_str='00c000c000c000c000c000c000c0') + time.sleep(0.05) + bmc.SetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0x90, size=14, hex_str='0080008000800080008000800080') + time.sleep(0.05) + + value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0xB0, size=14) + logging.info(f'00 CD B0: {value}') + value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=slot, bank=0x00, page=0xCD, reg_offset=0x90, size=14) + logging.info(f'00 CD 90: {value}') + + + # value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=0, bank=0x01, page=0xC2, reg_offset=0x88, size=16) + # logging.info(f'01 C2 88: {value}') + + + # value = bmc.GetOpticalModuleRegs(exp_id=exp_id, slot_id=0, bank=0x00, page=0xC5, reg_offset=0x80, size=128) + # logging.info(f'value: {value}') + + # # 确保 value 是偶数长度(安全处理) + # if len(value) % 2 != 0: + # logging.warning("Hex string has odd length, padding with a leading zero.") + # value = '0' + value + + # # 每两个字符一组,转为字节列表 + # bytes_list = [value[i:i+2] for i in range(0, len(value), 2)] + + # # 起始寄存器地址 + # start_reg = 0x80 + + # # 打印每个字节及其对应寄存器地址 + # for idx, byte_hex in enumerate(bytes_list): + # reg_addr = start_reg + idx + # logging.info(f"[0x{reg_addr:02X}] = 0x{byte_hex.upper()}") + + elif cmd == 'reg-rd': + rd_regs = param_list + all_lane_reg_values: List[LaneRegInfo] = reg_access_tool.read_opt_regs(exp_id, slot_list, lane_list, rd_regs) + if not all_lane_reg_values: + print("No register data read.") + else: + # 提取 slot 和 lane 顺序(只需一次) + slots = [item.slot_id for item in all_lane_reg_values] + lanes = [item.logic_lane for item in all_lane_reg_values] + + # 对 rd_regs 中每个寄存器,单独打印 + for reg_name in rd_regs: + values = [] + for item in all_lane_reg_values: + try: + value = getattr(item, reg_name) + values.append(value) + except AttributeError: + # 寄存器不存在,跳过该项 + continue + + if values: # 只有当找到至少一个有效值时才打印 + print(f"host: {host} exp: {exp_id} reg: {reg_name} slot: {slot_list} lane: {lane_list}") + print(f"value: {values}\n") + + elif cmd == 'reg-wt': + wt_regs = param_list + if 'ibias_runtime' in wt_regs: + for slot_id in slot_list: + bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0xB4, 0x88, 1, '01') + time.sleep(0.05) + + if 'ibias' in wt_regs: + for slot_id in slot_list: + bmc.SetOpticalModuleRegs(exp_id, slot_id, 0, 0xB4, 0x88, 1, '00') + time.sleep(0.05) + + reg_access_tool.write_opt_regs(exp_id, slot_list, lane_list, wt_regs, reg_wt_value) + + if 'mgc' in wt_regs: + for slot_id in slot_list: + reg_access_tool.write_confirm_reg(exp_id, slot_id) + + # if host == '10.57.216.91': + # for slot_id in slot_list: # 或 for slot_id in raw_data.keys() + # lane_values = raw_data_91[slot_id] + # logging.info(f"step 1, slot{slot_id} lane_values: {lane_values}") + # for lane_id in lane_list: + # value = lane_values[lane_id] + # logging.info("step 2") + # reg_access_tool.write_opt_regs(exp_id, [slot_id], [lane_id], ['swa_h','swa_m','swa_l'],str(value)) + + + # if host == '10.57.216.94': + # for slot_id in slot_list: # 或 for slot_id in raw_data.keys() + # lane_values = raw_data_94[slot_id] + # logging.info(f"step 1, slot{slot_id} lane_values: {lane_values}") + # for lane_id in lane_list: + # value = lane_values[lane_id] + # logging.info("step 2") + # reg_access_tool.write_opt_regs(exp_id, [slot_id], [lane_id], ['swa_h','swa_m','swa_l'],str(value)) + + elif cmd == 'tx-enable': + wt_regs = param_list + for slot in slot_list: + reg_access_tool.tx_enable(exp_id, slot) + + elif cmd == 'tx-disable': + wt_regs = param_list + for slot in slot_list: + reg_access_tool.tx_disable(exp_id, slot) + + elif cmd == 'opcurrent-tune': + all_lane_reg_values: List[LaneRegInfo] = reg_access_tool.read_opt_regs(exp_id, slot_list, lane_list, ['opcurrent']) + for lane_reg_value in all_lane_reg_values: + if lane_reg_value.opcurrent < 140: + logging.info(f'exp_id:{exp_id}, slot: {[lane_reg_value.slot_id]}, lane: {[lane_reg_value.logic_lane]}, opcurrent: {lane_reg_value.opcurrent}, need change to 130') + reg_access_tool.write_opt_regs(exp_id, [lane_reg_value.slot_id], [lane_reg_value.logic_lane], ['opcurrent'], '130') + + elif cmd == 'rssi-check': + RssiCheck(reg_access_tool, remote_reg_access_tool, bmc, host, exp_id, slot_list, ibias_rssi_map, route_name) + elif cmd == 'eye-check': + EyeCheck(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + elif cmd == 'err-check': + # logging.info(param) + # logging.info(param_list) + if rtmr_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in rtmr_list] + + is_show_all = False + if param == 'show-all': + is_show_all = True + + RecvErrCheck(host, remote_host, bmc, exp_id, slot_list, retimers, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool, False, is_show_all) + elif cmd == 'recv-check': + if rtmr_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in rtmr_list] + + RecvErrCheck(host, remote_host, bmc, exp_id, slot_list, retimers, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool, True) + elif cmd == 'inmpd-check': + inmpd_check(host, bmc, exp_id, slot_list, reg_access_tool) + elif cmd == 'tia-peak-tune': + wt_regs = param_list + TiaPeakTune(reg_access_tool, bmc, host, exp_id, slot_list, lane_list, wt_regs) + elif cmd == 'mgc-tune': + MgcTune(reg_access_tool, bmc, host, exp_id, slot_list, lane_list, reg_wt_value) + elif cmd == 'exp-cold-reset': + ExpColdReset(bmc, exp_id) + elif cmd in ['plot-mgc-vpeak', 'mgc-auto-tune', 'mgc-auto-tune2', 'tiapeak-auto-tune', 'highfreq-auto-tune', + 'lowfreq-auto-tune', 'opcurrent-auto-tune', 'opcurrent-auto-tune-test', 'rt-ibias', 'rt-mgc', 'mgc-manual-tune', 'mgc-calc-target-vpeak']: + logging.info(f'reg_table_file:{reg_table_file}') + + AutoTuneMGC(host, bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, + exp_id, slot_list, lane_list, reg_access_tool.parser, cmd, route_name) + + elif cmd == 'ibias-auto-tune': + logging.info(f'------------ param: {param}') + if rtmr_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in rtmr_list] + + if remote_bmc is None: + remote_bmc = bmc + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, route_name) + + if param == '': + preset = 9 + else: + preset = int(param) + + prbs_tool.ResetRetimerAndEnablePrbs(exp_id, retimers, preset) + if remote_bmc == bmc: + # AutoTuneIbias(host, bmc, exp_id, slot_list, lane_list, reg_access_tool.parser, route_name) + AutoTuneIbiasOnet(host, bmc, exp_id, slot_list, lane_list, bmc, reg_table_file, route_name, ibias_rssi_map) + else: + # bmc.TopoConfig(exp_id, '32') + # remote_bmc.TopoConfig(exp_id, '32') + # bmc.TopoConfig(exp_id, '32') + # remote_bmc.TopoConfig(exp_id, '32') + + AutoTuneIbiasOnet(host, bmc, exp_id, slot_list, lane_list, remote_bmc, reg_table_file, route_name, ibias_rssi_map) + + AutoTuneIbiasOnet(host, remote_bmc, exp_id, slot_list, lane_list, bmc, reg_table_file, route_name, ibias_rssi_map) + # elif cmd == 'lowfreq-auto-tune': + # AutoTuneLowFreq(host, bmc, exp_id, slot_list, reg_table_file, cmd) + elif cmd == "highfreq-tune": + wt_regs = param_list + TuneHighFreq(reg_access_tool, exp_id) + + elif cmd == 'swa-auto-tune': + SwaAutoTune(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + + elif cmd == 'swb-auto-tune': + SwbAutoTune(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + + elif cmd == 'mzm-auto-tune': + MzmAutoTune(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + elif cmd == 'rtmr-prbs': + if remote_host != '': + host_list = f'{host},{remote_host}' + else: + host_list = host + + cms_str = ["ocs_link_reset", "-i", f"{host_list}", "-e", f"{exp_id}"] + logging.info(f'---- cmd: {cms_str}') + result = subprocess.run(cms_str, timeout=30) + time.sleep(6) + if result.returncode == 0: + print("ocs link reset success") + else: + print("ocs link reset failed") + + logging.info(param) + logging.info(param_list) + if param_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in param_list] + + if remote_bmc is None: + remote_bmc = bmc + remote_reg_access_tool = reg_access_tool + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, route_name) + logging.info('--------step 2') + logging.info(f"------slot_list: {slot_list}") + prbs_tool.ResetRetimerAndEnablePrbs(exp_id, retimers) + elif cmd == 'rtmr-ctle-wt': + logging.info(f'--------- rtmr-ctle-wt, rtmr_list: {rtmr_list}') + + WriteRtmrCtle(host, exp_id, bmc, rtmr_list, lane_list, int(param)) + CommitRtmrCtle(exp_id, bmc, rtmr_list) + + elif cmd == 'rtmr-prbs-test': + if remote_host != '' and host != remote_host: + host_list = f'{host},{remote_host}' + else: + host_list = host + + # cms_str = ["ocs_link_reset", "-i", f"{host_list}", "-e", f"{exp_id}"] + # logging.info(f'---- cmd: {cms_str}') + # result = subprocess.run(cms_str, timeout=30) + # time.sleep(6) + # if result.returncode == 0: + # print("ocs link reset success") + # else: + # print("ocs link reset failed") + + logging.info(param) + logging.info(param_list) + if param_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in param_list] + + if remote_bmc is None: + remote_bmc = bmc + remote_reg_access_tool = reg_access_tool + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, route_name) + + logging.info(f"------slot_list: {slot_list}") + + logging.info("----------step 1: reset retimer") + prbs_tool.ResetRetimer(exp_id, retimers) + time.sleep(1) + logging.info("----------step 2: enable prbs") + + # all_rtmr_lane_settings: List[RetimerLaneSetting] = load_ctle_config(host, route_name, exp_id) + # for lane_setting in all_rtmr_lane_settings: + # WriteRtmrCtle(host, exp_id, bmc, [lane_setting.rtmr_id], [lane_setting.rtmr_lane], lane_setting.ctle) + # time.sleep(0.05) + + # if all_rtmr_lane_settings: + # CommitRtmrCtle(exp_id, bmc, retimers) + + # prbs_tool.EnableTxPrbsDebug(exp_id, retimers) + + prbs_tool.EnableTxPrbs(exp_id, retimers) + + prbs_tool.prbs_check(exp_id, retimers, True) + time.sleep(2) + prbs_tool.prbs_check(exp_id, retimers, False) + + elif cmd == 'ctle-auto-tune': + if remote_host != '' and host != remote_host: + host_list = f'{host},{remote_host}' + else: + host_list = host + + cms_str = ["ocs_link_reset", "-i", f"{host_list}", "-e", f"{exp_id}"] + logging.info(f'---- cmd: {cms_str}') + result = subprocess.run(cms_str, timeout=30) + time.sleep(6) + if result.returncode == 0: + print("ocs link reset success") + else: + print("ocs link reset failed") + + logging.info(param) + logging.info(param_list) + if rtmr_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in rtmr_list] + + if remote_bmc is None: + remote_bmc = bmc + remote_reg_access_tool = reg_access_tool + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, route_name) + + logging.info(f"------slot_list: {slot_list}") + + logging.info("----------step 1: reset retimer") + prbs_tool.ResetRetimer(exp_id, retimers) + time.sleep(1) + logging.info("----------step 2: enable prbs") + + # all_rtmr_lane_settings: List[RetimerLaneSetting] = load_ctle_config(host, route_name, exp_id) + # for lane_setting in all_rtmr_lane_settings: + # # WriteRtmrCtle(host, exp_id, bmc, [lane_setting.rtmr_id], [lane_setting.rtmr_lane], 0) + # WriteRtmrCtle(host, exp_id, bmc, [lane_setting.rtmr_id], [lane_setting.rtmr_lane], lane_setting.ctle) + # time.sleep(0.05) + + + tune_value_list = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] + # tune_value_list = [0, 18] + lane_data_list: List[LaneErrInfo] = [] + + topo_map = TopoMappingParser("/xz/gyou/nexusbench/main_data/topo_mapping.yaml") + topo_map.parse() + + for slot_id in [0, 1, 2, 3, 4, 5, 6, 7]: + for lane in [0, 1, 2, 3, 4, 5, 6, 7]: + tmp_err_map = {value: -1 for value in tune_value_list} + + result = topo_map.get_phys_rtmr_by_slot(slot_id, lane, route_name) + if result: + success, rt_phys_id, rt_phys_lane = result + if success == False: + logging.error(f'get_phys_rtmr_by_slot failed, result: {success}') + return False + + lane_info = LaneErrInfo( + slot_id=slot_id, + logic_lane=lane, + rt_phys_id=rt_phys_id, + rt_phys_lane=rt_phys_lane, + tmp_err_map=tmp_err_map + ) + + lane_data_list.append(lane_info) + if param == '': + logging.info(f"preset is empty, please set preset by param") + preset_list = [int(param)] + for preset in preset_list: + for ctle in tune_value_list: + for rtmr in retimers: + for rt_lane in range(16): + WriteRtmrCtle(host, exp_id, bmc, [rtmr], [rt_lane], ctle) + time.sleep(0.05) + + # if all_rtmr_lane_settings: + CommitRtmrCtle(exp_id, bmc, retimers) + + prbs_tool.EnableTxPrbs(exp_id, retimers, preset) + + prbs_tool.prbs_check(exp_id, retimers, True) + time.sleep(5) + prbs_results = prbs_tool.prbs_check(exp_id, retimers, False) + # logging.info(f'-------------- prbs_results: {prbs_results}') + + for prbs in prbs_results: + logging.info(f'rtmr_id: {prbs.rtmr_id}, lane: {prbs.rtmr_lane}, route: {route_name}') + slot = topo_map.get_local_slot_by_retimer(prbs.rtmr_id, prbs.rtmr_lane, route_name) + + if not slot: + logging.error(f'local slot is none!') + return False + slot_id, lane_id = slot + + for info in lane_data_list: + if info.slot_id == slot_id and info.logic_lane == lane_id: + info.preset = preset + # logging.info(f'--------- start modify, value: {value}, prbs.locked: {prbs.locked}') + if prbs.locked: + info.tmp_err_map[ctle] = prbs.err_count + else: + info.tmp_err_map[ctle] = 69999 + # logging.info(f'------prbs unlock, {value}: {info.tmp_err_map[value]}') + + # logging.info(f'info.tmp_err_map: {info.tmp_err_map}') + break + + for rtmr in retimers: + bmc.CmdVendorCommand(exp_id, f'rtmr {rtmr} phy full') + + time.sleep(1) + rd_regs = ['mgc'] + slot_list = [0,1,2,3,4,5,6,7] + all_lane_reg_values: List[LaneRegInfo] = reg_access_tool.read_opt_regs(exp_id, slot_list, [0,1,2,3,4,5,6,7], rd_regs) + if not all_lane_reg_values: + print("No register data read.") + else: + # 提取 slot 和 lane 顺序(只需一次) + slots = [item.slot_id for item in all_lane_reg_values] + lanes = [item.logic_lane for item in all_lane_reg_values] + + # 对 rd_regs 中每个寄存器,单独打印 + for reg_name in rd_regs: + values = [getattr(item, reg_name) for item in all_lane_reg_values] + logging.info(f"host: {host} exp: {exp_id} reg: {reg_name} slot: {slot_list} lane: {lane_list}") + logging.info(f"value: {values}\n") + + print_general_err_matrix(host, exp_id, lane_data_list, tune_value_list) + + elif cmd == "rtmr-reset": + if remote_host != '': + host_list = f'{host},{remote_host}' + else: + host_list = host + + logging.info(param) + logging.info(param_list) + if param_list == ['']: + retimers = [1,2,3,4] + else: + retimers = [int(x) for x in param_list] + + if remote_bmc is None: + remote_bmc = bmc + remote_reg_access_tool = reg_access_tool + + prbs_tool = PrbsTool(bmc, remote_bmc, reg_access_tool, remote_reg_access_tool, route_name) + logging.info('--------step 2') + logging.info(f"------slot_list: {slot_list}") + # prbs_tool.ResetRetimer(exp_id, retimers) + # logging.info(f'host: {host_list}') + # with ThreadPoolExecutor(max_workers=len(host_list)) as executor: + # executor.map(process_rtmr_waram_reset, host_list) + rtmr_reset_and_port_reset(bmc, exp_id) + + elif cmd == "vcmd": + bmc.CmdVendorCommand(exp_id, param) + elif cmd == "eq-auto-tune": + logging.info(f'------------ param: {param}') + AutoTuneEq(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + + elif cmd == "calc-ctle-map": + logging.info(f'------------ param: {param}') + CalcCtleMap(host, bmc, exp_id, slot_list, lane_list, remote_bmc, route_name, reg_access_tool, remote_reg_access_tool) + elif cmd == "topo-config": + bmc.TopoConfig(exp_id, param) + elif cmd == "exp-stat": + bmc.GetExpStatus(exp_id) + elif cmd == 'reg-save': + regs = param_list + logging.info(f'--------param_list:{param_list}') + if regs == ['']: + regs = ['mgc', 'vpeak', 'tia_peak', 'ibias', 'ipcurrent', 'opcurrent', + 'lowfreq_eq', 'highfreq_eq', 'vgc_set', 'drv_vpeak', 'rssi', + 'swa_onoc_h', 'swa_onoc_m', 'swa_onoc_l', 'swa_onet_h', 'swa_onet_m', 'swa_onet_l', + 'swb_oneta_h', 'swb_oneta_m', 'swb_oneta_l', 'swb_onetb_h', 'swb_onetb_m', 'swb_onetb_l'] + SaveRegsToFile(reg_access_tool, host, exp_id, slot_list, route_name, lane_list, regs, sl_file) + elif cmd == 'reg-load': + regs = param_list + logging.info(f'--------param_list:{param_list}') + # if regs == ['']: + # logging.error('regs is empty') + # regs = ['mgc', 'tia_peak', 'ibias', 'ipcurrent', 'opcurrent', + # 'lowfreq_eq', 'highfreq_eq', 'vgc_set', 'drv_vpeak'] + LoadRegsToFile(reg_access_tool, host, exp_id, slot_list, route_name, regs, sl_file) + elif cmd == 'tune-rd': + rtmr_id = int(param_list[0]) + rtmr_lane = int(param_list[1]) + TuneView(host, bmc, exp_id, slot_list, lane_list, remote_bmc, + route_name, reg_access_tool, remote_reg_access_tool, + rtmr_id, rtmr_lane) + elif cmd == 'tune-wt': + rtmr_id = int(param_list[0]) + rtmr_lane = int(param_list[1]) + reg_name_value = reg_wt_value.split('=') + TuneWrite(host, bmc, exp_id, slot_list, lane_list, remote_bmc, + route_name, reg_access_tool, remote_reg_access_tool, + rtmr_id, rtmr_lane, reg_name_value[0], int(reg_name_value[1])) + elif cmd == 'set-power': + SetPower(host, exp_id, bmc, param) + else: + logging.error(f"Invalid command: {cmd}") + + exp_util.unlock(exp_id) + logging.info(f'----------process_host {host} exp {exp_id} unlock') + + if cmd in ['ibias-auto-tune', 'eq-auto-tune', 'tune-rd', 'tune-wt', 'rssi-check', 'err-check', 'rtmr-prbs', 'rtmr-prbs-test', 'ctle-auto-tune', 'mgc-auto-tune', + 'mgc-auto-tune2', 'lowfreq-auto-tune', 'highfreq-auto-tune', 'opcurrent-auto-tune', 'opcurrent-auto-tune-test', 'tiapeak-auto-tune'] and remote_ip != '': + if remote_exp_util: + remote_exp_util.unlock(exp_id) + logging.info(f'----------process dst host {remote_host} exp {exp_id} unlock') + +def expand_ip(ip: str) -> str: + if '.' in ip: + return ip # 已经是完整 IP,直接返回 + try: + last_octet = int(ip) + if 0 <= last_octet <= 255: + return f"10.57.216.{last_octet}" + else: + raise ValueError(f"Invalid IP last octet: {last_octet}") + except ValueError as e: + raise ValueError(f"Invalid IP format: {ip}, must be x.x.x.x or 0-255") from e + + + + +if __name__ == "__main__": + Logger.init_logging(log_folder='logs', level=logging.INFO) + parser = argparse.ArgumentParser(description="XZtech OCS Diag Tool (v0.2@20260106)") + parser.add_argument("--host", "-i", type=str, required=False, default='', help="") + parser.add_argument("--remote_host", "-r", type=str, required=False, default='', help="") + parser.add_argument("--os_host", "-o", type=str, required=False, default='', help="") + parser.add_argument("--exp", "-e", type=str, required=True, default='', help="") + parser.add_argument("--slot", "-s", type=str, default='0', help="") + parser.add_argument("--rtmr", "-rt", type=str, default='all', help="") + parser.add_argument("--lane", "-l", type=str, default='all', help="default all lane") + parser.add_argument("--cmd", "-c", required=True, type=str, default='', + help="" ) + # parser.add_argument("--cmd", "-c", required=True, type=str, default='', + # help="exp-cold-reset, exp_stat, rtmr-prbs, rtmr-reset, reg-rd, reg-wt, vcmd, \ + # topo-config, mgc-auto-tune, mgc-manual-tune, mgc-calc-target-vpeak, \ + # ibias-auto-tune, reg-save, reg-load, lowfreq-auto-tune, highfreq-tune, tia-peak-tune, eq-auto-tune, tune-view" ) + parser.add_argument("--param", "-p", type=str, default='', help="param for cmd") + parser.add_argument("--value", "-v", type=str, default='', help="value of reg-rd") + parser.add_argument("--reg_table", type=str, default='', help="") + parser.add_argument("--sl_file", type=str, default='', + help="default") + args = parser.parse_args() + + # config_file = args.rw_config + reg_table_file = args.reg_table + # logging.info(reg_table_file) + + sl_file = args.sl_file + # logging.info(f'-------save load file: {sl_file}') + + host_list = [expand_ip(h.strip()) for h in args.host.split(',') if h.strip()] + # logging.info(f'------ host_list: {host_list}') + + os_host_list = [expand_ip(h.strip()) for h in args.os_host.split(',') if h.strip()] + # logging.info(f'------ os_host_list: {os_host_list}') + + if hasattr(args, 'remote_host') and args.remote_host: + remote_ip = expand_ip(args.remote_host.strip()) + logging.info(f'------ remote_ip: {remote_ip}') + else: + remote_ip = '' + + exp_list = args.exp.split(',') + + cmd = args.cmd + + if args.slot == 'all': + slot_list = [0,1,2,3,4,5,6,7] + else: + slot_list = list(map(int, args.slot.split(','))) + + if args.rtmr == 'all': + rtmr_list = [1,2,3,4] + else: + rtmr_list = list(map(int, args.rtmr.split(','))) + logging.info(rtmr_list) + + if args.lane == 'all': + lane_list = [0,1,2,3,4,5,6,7] + if cmd == 'rtmr-ctle-wt': + lane_list = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] + else: + lane_list = list(map(int, args.lane.split(','))) + + + # logging.info(f'------slot_list:{slot_list}') + + param = '' + param_list = args.param.split(',') + if len(param_list) == 1: + param = args.param + + reg_wt_value = args.value + + if cmd == 'reg-wt' and reg_wt_value == '': + logging.error('reg write value is empty!') + sys.exit(1) + + # with ThreadPoolExecutor(max_workers=len(host_list)) as executor: + # executor.map(process_host, host_list) + + if cmd == 'link-train': + if param == '': + logging.error('topo name is empty!') + sys.exit() + else: + link_train(host_list, os_host_list, exp_list, f'mesh_{param}.json') + sys.exit() + elif cmd == 'gpu-reset': + gpu_reset(os_host_list) + sys.exit() + elif cmd == 'gpu-cto-disable': + gpu_cto_disable(os_host_list) + sys.exit() + + with ThreadPoolExecutor(max_workers=1) as executor: + func_with_params = partial(process_host, reg_table_file=reg_table_file, exp_list=exp_list, + slot_list=slot_list, rtmr_list=rtmr_list, lane_list=lane_list, cmd=cmd, param=param, + reg_wt_value=reg_wt_value, sl_file=sl_file) + executor.map(func_with_params, host_list) + + + + + + + + + +