diff --git a/ocs/note.md b/ocs/note.md index ac632c2..3bb3999 100644 --- a/ocs/note.md +++ b/ocs/note.md @@ -78,527 +78,749 @@ scp -o "BatchMode=no" -o "StrictHostKeyChecking=no" -i pass.txt /usr/bin/ocs_lin +from dataclasses import dataclass +import re +from typing import List, Optional, Dict, DefaultDict +import time +from enum import Enum +import logging +from collections import defaultdict +from parser.topology_parser import TopoMappingParser +from parser.transceiver_config_parser import TransceiverConfigParser +from toolbox.opt_reg_access_tool import OptRegAccessTool +from gpu.biren.exp_util import DevWhiteRiverExp +from parser.ibias_rssi_map_parser import IbiasRssiMapParser + +logger = logging.getLogger(__name__) + +# -------------------------------------------------- +# Eye Issue Definition (Corrected Logic) +# -------------------------------------------------- + +class OptReg(Enum): + MGC = '' + +class EyeIssue(Enum): + EYE_NORMAL = 0 + EYE_TOO_SMALL = 1 # 垂直开口太小: sum(|top|, |down|) < 30 + EYE_TOO_LARGE = 2 + EYE_TOO_DOWN = 3 # 眼图偏低: top < 15 + EYE_TOO_UP = 4 # 眼图偏高: |down| < 15 (i.e., down > -15) + +@dataclass +class EyeData: + rt_phys_index: int = -1 + phys_lane_index: int = -1 + rt_index: int = -1 + lane_index: int = -1 + down: float = -1 # mV (usually negative) + top: float = -1 # mV (usually positive) + left: float = -1 # UI + right: float = -1 # UI + issue: EyeIssue = EyeIssue.EYE_NORMAL + + def __str__(self): + return (f"RTMR{self.rt_index} EYE_A{self.lane_index:02d}: " + f"({self.down:.1f}, {self.top:.1f}) mV | " + f"({self.left:.2f}, {self.right:.2f}) UI") + + @property + def vertical_amplitude(self) -> float: + return abs(self.down) + abs(self.top) + + @property + def horizontal_amplitude(self) -> float: + return abs(self.left) + abs(self.right) + + @property + def quality_score(self) -> float: + return self.vertical_amplitude + + def determine_issue(self) -> None: + if self.vertical_amplitude < 30: + self.issue = EyeIssue.EYE_TOO_SMALL + elif self.vertical_amplitude > 220: + self.issue = EyeIssue.EYE_TOO_LARGE + elif self.top < 15: + self.issue = EyeIssue.EYE_TOO_DOWN + elif self.down > -15: # 因为 down 是负值,> -15 表示离 0 太近(太高) + self.issue = EyeIssue.EYE_TOO_UP + else: + self.issue = EyeIssue.EYE_NORMAL + + +# -------------------------------------------------- +# 🔧 EQ Tuning Tool +# -------------------------------------------------- + +class EqTuneTool: + def __init__(self, local_bmc: DevWhiteRiverExp, remote_bmc: DevWhiteRiverExp, + local_reg_tool: OptRegAccessTool, remote_reg_tool: OptRegAccessTool, + ibias_rssi_map: IbiasRssiMapParser, route_name: str): + self.local_bmc = local_bmc + self.remote_bmc = remote_bmc + self.local_reg_tool = local_reg_tool + self.remote_reg_tool = remote_reg_tool + self.ibias_rssi_map = ibias_rssi_map + self.route_name = route_name + self.topo_map = TopoMappingParser("./main_data/topo_mapping.yaml") + self.topo_map.parse() + + def eq_auto_tune(self, exp_id: int): + + cmd = 'ver' + raw_output = self.local_bmc.CmdVendorCommand(exp_id, cmd) + logging.info(raw_output) + + raw_tia_peak_value = self.local_reg_tool.read_tia_peak_reg(exp_id, 2, 0) + logging.info(f'Test Initial tia_peak_value: {raw_tia_peak_value}') + + logger.info(f"Starting auto tune for exp id: {exp_id}") + # retimers = [1, 2, 3, 4] + retimers = [3] + for retimer_index in retimers: + bad_eyes = self.get_issue_eye_diagram(exp_id, retimer_index) + time.sleep(1) + if bad_eyes: + self.process_worst_eyes(exp_id, retimer_index, bad_eyes) + else: + logger.info(f"No eye issues found on RTMR{retimer_index}") + + def process_worst_eyes(self, exp_id: int, retimer_index: int, bad_eyes: List[EyeData]) -> None: + logger.warning(f"Detected eye issues on RTMR{retimer_index}:") + + for eye in bad_eyes: + logger.warning( + f"RTMR {eye.rt_index} Lane {eye.lane_index:02d}: {eye.issue.name} " + f"(Vertical: {eye.vertical_amplitude:.1f}mV, Top={eye.top:.1f}, Down={eye.down:.1f})" + ) + + for eye in bad_eyes: + logger.warning( + f"Starting Process RTMR {eye.rt_index} Lane {eye.lane_index:02d}: {eye.issue.name} " + f"(Vertical: {eye.vertical_amplitude:.1f}mV, Top={eye.top:.1f}, Down={eye.down:.1f})" + ) + tmp_eye = eye + max_retry_cout = 2 + try_count = 0 + while tmp_eye.issue != EyeIssue.EYE_NORMAL and try_count < max_retry_cout: + logging.info(f'------try count: {try_count}') + if tmp_eye.issue == EyeIssue.EYE_TOO_SMALL: + self.process_small_eye(exp_id, retimer_index, eye) + + if tmp_eye.issue == EyeIssue.EYE_TOO_DOWN: + self.process_down_eye(exp_id, retimer_index, eye) + elif tmp_eye.issue == EyeIssue.EYE_TOO_UP: + self.process_up_eye(exp_id, retimer_index, eye) + + # if tmp_eye.issue == EyeIssue.EYE_TOO_LARGE: + # logging.info(f"Eye issue: {eye.issue.name}") + + try_count +=1 + + # # if tmp_eye.issue == EyeIssue.EYE_TOO_LARGE: + # # 如果rssi很大,那适当减少,减少ibias + # # slot = self.topo_map.get_remote_slot_by_retimer(eye.rt_index, eye.lane_index) + # # if slot is None: + # # break + # # slot_id = slot[0] + # # lane_id = slot[1] + # # ibias_value = self.remote_reg_tool. + + def process_small_eye(self, exp_id: int, retimer_index: int, eye: EyeData): + logger.info(f"Starting small eye issue processing for RTMR{retimer_index} lane {eye.lane_index}, adjusting ibias") + + # 获取slot信息 + remote_slot = self.topo_map.get_remote_slot_by_retimer(eye.rt_index, eye.lane_index) + local_slot = self.topo_map.get_local_slot_by_retimer(eye.rt_index, eye.lane_index) + if remote_slot is None or local_slot is None: + logger.error(f"Invalid slot for RTMR{retimer_index} lane {eye.lane_index}") + return eye + + remote_slot_id = remote_slot[0] + remote_lane_id = remote_slot[1] + local_slot_id = local_slot[0] + local_lane_id = local_slot[1] + + logger.info(f"Processing small eye for RTMR{eye.rt_index} lane {eye.lane_index}: " + f"remote_slot=({remote_slot_id},{remote_lane_id}), " + f"local_slot=({local_slot_id},{local_lane_id})") + + # 调节ibias + logger.info(f"Adjusting ibias for RTMR{eye.rt_index} lane {eye.lane_index}") + eye = self.adjust_ibias_for_small_eye(exp_id, eye, remote_slot_id, remote_lane_id, local_slot_id, local_lane_id) + + # 如果依然很小,调节opcurrent + if eye.issue == EyeIssue.EYE_TOO_SMALL: + logger.info(f"Eye still too small for RTMR{retimer_index} lane {eye.lane_index}, adjusting opcurrent") + eye = self.adjust_opcurrent_for_small_down_eye(exp_id, eye, remote_slot_id, remote_lane_id) + # eye = self.adjust_opcurrent_for_up_eye(exp_id, eye, remote_slot_id, remote_lane_id) + + # 如果依然很小,调节mgc + if eye.issue == EyeIssue.EYE_TOO_SMALL: + logger.info(f"Eye still too small for RTMR{retimer_index} lane {eye.lane_index}, adjusting mgc") + eye = self.adjust_mgc_for_small_eye(exp_id, eye, local_slot_id, local_lane_id) + + logger.info(f"Completed small eye processing for RTMR{retimer_index} lane {eye.lane_index}, " + f"final issue: {eye.issue.name}") + return eye + + def process_down_eye(self, exp_id: int, retimer_index: int, eye: EyeData): + logging.info(f"Starting down eye issue processing for RTMR{retimer_index} lane {eye.lane_index}") + + # 获取slot信息 + remote_slot = self.topo_map.get_remote_slot_by_retimer(eye.rt_index, eye.lane_index) + local_slot = self.topo_map.get_local_slot_by_retimer(eye.rt_index, eye.lane_index) + if remote_slot is None or local_slot is None: + logger.error(f"Invalid slot for RTMR{retimer_index} lane {eye.lane_index}") + return eye + + remote_slot_id = remote_slot[0] + remote_lane_id = remote_slot[1] + local_slot_id = local_slot[0] + local_lane_id = local_slot[1] + + logger.info(f"Processing down eye for RTMR{eye.rt_index} lane {eye.lane_index}: " + f"remote_slot=({remote_slot_id},{remote_lane_id}), " + f"local_slot=({local_slot_id},{local_lane_id})") + + # 先调节tia_peak(值调小) + logger.info(f"Adjusting tia_peak for RTMR{eye.rt_index} lane {eye.lane_index}") + eye = self.adjust_tia_peak_for_down_eye(exp_id, eye, local_slot_id, local_lane_id) + + # 如果依然偏下,调节high_freq(值调小) + if eye.issue == EyeIssue.EYE_TOO_DOWN: + logger.info(f"Eye still too down for RTMR{retimer_index} lane {eye.lane_index}, adjusting high_freq") + eye = self.adjust_high_freq_for_down_eye(exp_id, eye, remote_slot_id, remote_lane_id) + + # 如果依然偏下,调节opcurrent + if eye.issue == EyeIssue.EYE_TOO_DOWN: + logger.info(f"Eye still too down for RTMR{retimer_index} lane {eye.lane_index}, adjusting opcurrent") + eye = self.adjust_opcurrent_for_small_down_eye(exp_id, eye, remote_slot_id, remote_lane_id) + + logger.info(f"Completed down eye processing for RTMR{retimer_index} lane {eye.lane_index}, " + f"final issue: {eye.issue.name}") + return eye + + def process_up_eye(self, exp_id: int, retimer_index: int, eye: EyeData): + logging.info(f"Starting up eye issue processing for RTMR{retimer_index} lane {eye.lane_index}") + + # 获取slot信息 + remote_slot = self.topo_map.get_remote_slot_by_retimer(eye.rt_index, eye.lane_index) + local_slot = self.topo_map.get_local_slot_by_retimer(eye.rt_index, eye.lane_index) + if remote_slot is None or local_slot is None: + logger.error(f"Invalid slot for RTMR{retimer_index} lane {eye.lane_index}") + return eye + + remote_slot_id = remote_slot[0] + remote_lane_id = remote_slot[1] + local_slot_id = local_slot[0] + local_lane_id = local_slot[1] + + logger.info(f"Processing up eye for RTMR{eye.rt_index} lane {eye.lane_index}: " + f"remote_slot=({remote_slot_id},{remote_lane_id}), " + f"local_slot=({local_slot_id},{local_lane_id})") + + # 先调节tia_peak(值调大) + logger.info(f"Adjusting tia_peak for RTMR{eye.rt_index} lane {eye.lane_index}") + eye = self.adjust_tia_peak_for_up_eye(exp_id, eye, local_slot_id, local_lane_id) + + # 如果依然偏高,调节high_freq(值调大) + if eye.issue == EyeIssue.EYE_TOO_UP: + logger.info(f"Eye still too up for RTMR{retimer_index} lane {eye.lane_index}, adjusting high_freq") + eye = self.adjust_high_freq_for_up_eye(exp_id, eye, remote_slot_id, remote_lane_id) + + # 如果依然偏高,调节opcurrent(值调小) + if eye.issue == EyeIssue.EYE_TOO_UP: + logger.info(f"Eye still too up for RTMR{retimer_index} lane {eye.lane_index}, adjusting opcurrent") + eye = self.adjust_opcurrent_for_up_eye(exp_id, eye, remote_slot_id, remote_lane_id) + + logger.info(f"Completed up eye processing for RTMR{retimer_index} lane {eye.lane_index}, " + f"final issue: {eye.issue.name}") + return eye + + def adjust_ibias_for_small_eye(self, exp_id: int, eye: EyeData, remote_slot_id: int, remote_lane_id: int, + local_slot_id: int, local_lane_id: int) -> EyeData: + ibias_range = [2000, 3200] + rssi_range = [5000, 10000] + ibias_value = self.remote_reg_tool.read_ibias_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial ibias_value: {ibias_value}') + if ibias_value == 0: + logging.warning(f'read ibias reg again') + ibias_value = self.remote_reg_tool.read_ibias_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial ibias_value: {ibias_value}') + + while ibias_value < ibias_range[1]: + # 根据当前值确定步长 + step = 300 if ibias_value < 2500 else 150 + ibias_value += step + self.remote_reg_tool.write_ibias_reg(exp_id, remote_slot_id, remote_lane_id, ibias_value) + logging.info(f'Adjusted ibias to {ibias_value} with step {step}') + + ibias_lane = self.remote_reg_tool.get_ibias_by_logic_lane(remote_lane_id) + rssi_lanes = self.ibias_rssi_map.get_rssi_lane(self.route_name, ibias_lane) + if rssi_lanes is None: + logging.error('RSSI lanes is none') + break + + rssi1_value = self.local_reg_tool.read_rssi_reg(exp_id, local_slot_id, rssi_lanes[0]) + rssi2_value = self.local_reg_tool.read_rssi_reg(exp_id, local_slot_id, rssi_lanes[1]) + logging.info(f'RSSI values: rssi1={rssi1_value}, rssi2={rssi2_value}') + + is_margin = False + if rssi1_value > rssi_range[1] or rssi2_value > rssi_range[1]: + # 回退一步 + self.remote_reg_tool.write_ibias_reg(exp_id, remote_slot_id, remote_lane_id, ibias_value - step) + logging.info(f'RSSI margin exceeded, rollback ibias to {ibias_value - step}') + is_margin = True + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After ibias adjustment, eye issue: {eye.issue.name}') + if eye.issue != EyeIssue.EYE_TOO_SMALL or is_margin: + break + + logging.info(f'Final ibias adjustment result: ibias={ibias_value}, eye issue={eye.issue.name}') + return eye + + def adjust_opcurrent_for_small_down_eye(self, exp_id: int, eye: EyeData, remote_slot_id: int, remote_lane_id: int) -> EyeData: + raw_eye_issue = eye.issue + raw_opcurrent_value = self.remote_reg_tool.read_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial opcurrent_value: {raw_opcurrent_value}') + op_range = [100, 190] + opcurrent_value = raw_opcurrent_value + + while opcurrent_value < op_range[1]: + step = 10 + opcurrent_value += step + if opcurrent_value > op_range[1]: + opcurrent_value = op_range[1] + + self.remote_reg_tool.write_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id, opcurrent_value) + logging.info(f'Adjusted opcurrent to {opcurrent_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After opcurrent adjustment, eye issue: {eye.issue.name}') + + if eye.issue != raw_eye_issue or opcurrent_value == op_range[1]: + break + + # 如果没有改善,恢复原始值 + if eye.issue == raw_eye_issue: + self.remote_reg_tool.write_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id, raw_opcurrent_value) + logging.info(f'No improvement, rollback opcurrent to {raw_opcurrent_value}') + else: + logging.info(f'opcurrent adjustment successful, final value: {opcurrent_value}') + + return eye + + def adjust_mgc_for_small_eye(self, exp_id: int, eye: EyeData, local_slot_id: int, local_lane_id: int) -> EyeData: + raw_mgc_value = self.local_reg_tool.read_mgc_reg(exp_id, local_slot_id, local_lane_id) + logging.info(f'Initial mgc_value: {raw_mgc_value}') + mgc_range = [raw_mgc_value, raw_mgc_value + 10] + mgc_value = raw_mgc_value + + while mgc_value < mgc_range[1]: + step = 2 + mgc_value += step + self.local_reg_tool.write_mgc_reg(exp_id, local_slot_id, local_lane_id, mgc_value) + logging.info(f'Adjusted mgc to {mgc_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After mgc adjustment, eye issue: {eye.issue.name}') + + if eye.issue != EyeIssue.EYE_TOO_SMALL: + logging.info(f'mgc adjustment successful, final value: {mgc_value}') + return eye + + # 如果没有改善,恢复原始值 + self.local_reg_tool.write_mgc_reg(exp_id, local_slot_id, local_lane_id, raw_mgc_value) + logging.info(f'No improvement, rollback mgc to {raw_mgc_value}') + return eye + + def adjust_tia_peak_for_down_eye(self, exp_id: int, eye: EyeData, local_slot_id: int, local_lane_id: int) -> EyeData: + raw_tia_peak_value = self.local_reg_tool.read_tia_peak_reg(exp_id, local_slot_id, local_lane_id) + logging.info(f'Initial tia_peak_value: {raw_tia_peak_value}') + tia_peak_range = [0, 200] + tia_peak_value = raw_tia_peak_value + + while tia_peak_value > tia_peak_range[0]: + step = 20 + tia_peak_value -= step + if tia_peak_value < tia_peak_range[0]: + tia_peak_value = tia_peak_range[0] + + self.local_reg_tool.write_tia_peak_reg(exp_id, local_slot_id, local_lane_id, tia_peak_value) + logging.info(f'Adjusted tia_peak to {tia_peak_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After tia_peak adjustment, eye issue: {eye.issue.name}') + + if eye.issue != EyeIssue.EYE_TOO_DOWN or tia_peak_value == tia_peak_range[0]: + break + + logging.info(f'Final tia_peak adjustment result: tia_peak={tia_peak_value}, eye issue={eye.issue.name}') + return eye + + def adjust_high_freq_for_down_eye(self, exp_id: int, eye: EyeData, remote_slot_id: int, remote_lane_id: int) -> EyeData: + raw_high_freq_value = self.remote_reg_tool.read_high_freq_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial high_freq_value: {raw_high_freq_value}') + high_freq_range = [0, 150] + high_freq_value = raw_high_freq_value + + while high_freq_value > high_freq_range[0]: + step = 20 + high_freq_value -= step + if high_freq_value < high_freq_range[0]: + high_freq_value = high_freq_range[0] + + self.remote_reg_tool.write_high_freq_reg(exp_id, remote_slot_id, remote_lane_id, high_freq_value) + logging.info(f'Adjusted high_freq to {high_freq_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After high_freq adjustment, eye issue: {eye.issue.name}') + + if eye.issue != EyeIssue.EYE_TOO_DOWN or high_freq_value == high_freq_range[0]: + break + + logging.info(f'Final high_freq adjustment result: high_freq={high_freq_value}, eye issue={eye.issue.name}') + return eye + + def adjust_tia_peak_for_up_eye(self, exp_id: int, eye: EyeData, local_slot_id: int, local_lane_id: int) -> EyeData: + raw_tia_peak_value = self.local_reg_tool.read_tia_peak_reg(exp_id, local_slot_id, local_lane_id) + logging.info(f'Initial tia_peak_value: {raw_tia_peak_value}') + tia_peak_range = [0, 200] + max_value = tia_peak_range[1] + tia_peak_value = raw_tia_peak_value + + while tia_peak_value < max_value: + step = 20 + tia_peak_value += step + if tia_peak_value > max_value: + tia_peak_value = max_value + + self.local_reg_tool.write_tia_peak_reg(exp_id, local_slot_id, local_lane_id, tia_peak_value) + logging.info(f'Adjusted tia_peak to {tia_peak_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After tia_peak adjustment, eye issue: {eye.issue.name}') + + if eye.issue != EyeIssue.EYE_TOO_UP or tia_peak_value == max_value: + break + + logging.info(f'Final tia_peak adjustment result: tia_peak={tia_peak_value}, eye issue={eye.issue.name}') + return eye + + def adjust_high_freq_for_up_eye(self, exp_id: int, eye: EyeData, remote_slot_id: int, remote_lane_id: int) -> EyeData: + raw_high_freq_value = self.remote_reg_tool.read_high_freq_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial high_freq_value: {raw_high_freq_value}') + high_freq_value = raw_high_freq_value + high_freq_range = [0, 150] + while high_freq_value < high_freq_range[1]: + step = 20 + high_freq_value += step + if high_freq_value > high_freq_range[1]: + high_freq_value = high_freq_range[1] + + self.remote_reg_tool.write_high_freq_reg(exp_id, remote_slot_id, remote_lane_id, high_freq_value) + logging.info(f'Adjusted high_freq to {high_freq_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After high_freq adjustment, eye issue: {eye.issue.name}') + + if eye.issue != EyeIssue.EYE_TOO_UP or high_freq_value == high_freq_range[1]: + break + + logging.info(f'Final high_freq adjustment result: high_freq={high_freq_value}, eye issue={eye.issue.name}') + return eye + + def adjust_opcurrent_for_up_eye(self, exp_id: int, eye: EyeData, remote_slot_id: int, remote_lane_id: int) -> EyeData: + raw_eye_issue = eye.issue + raw_opcurrent_value = self.remote_reg_tool.read_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id) + logging.info(f'Initial opcurrent_value: {raw_opcurrent_value}') + op_range = [0, 190] + opcurrent_value = raw_opcurrent_value + + while opcurrent_value > op_range[0]: + step = 10 + opcurrent_value -= step + if opcurrent_value < op_range[0]: + opcurrent_value = op_range[0] + + self.remote_reg_tool.write_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id, opcurrent_value) + logging.info(f'Adjusted opcurrent to {opcurrent_value}') + + eye = self.get_eye_diagram_per_lane(exp_id, eye.rt_phys_index, eye.phys_lane_index) + logging.info(f'After opcurrent adjustment, eye issue: {eye.issue.name}') + + if eye.issue != raw_eye_issue or opcurrent_value == op_range[0]: + break + + # 如果没有改善,恢复原始值 + if eye.issue == raw_eye_issue: + self.remote_reg_tool.write_opcurrent_reg(exp_id, remote_slot_id, remote_lane_id, raw_opcurrent_value) + logging.info(f'No improvement, rollback opcurrent to {raw_opcurrent_value}') + else: + logging.info(f'opcurrent adjustment successful, final value: {opcurrent_value}') + + return eye + + def get_issue_eye_diagram(self, exp_id: int, retimer_index: int, attempts: int = 1) -> List[EyeData]: + all_measurements: DefaultDict[int, List[EyeData]] = defaultdict(list) + + for attempt in range(1, attempts + 1): + logger.info(f"RTMR{retimer_index} measurement attempt {attempt}/{attempts}") + eye_data_list = self.get_eye_diagram(exp_id, retimer_index) + + if not eye_data_list: + logger.error(f"Attempt {attempt} failed for RTMR{retimer_index}") + continue + + for eye_data in eye_data_list: + all_measurements[eye_data.lane_index].append(eye_data) + + if not all_measurements: + logger.error(f"No valid measurements for RTMR{retimer_index}") + return [] + + worst_eyes: List[EyeData] = [] + for lane_idx, measurements in all_measurements.items(): + logging.debug(f'---------- lan_inx: {lane_idx}, measurements: {measurements}') + worst_eye = self.determine_issue(measurements) + worst_eyes.append(worst_eye) + + bad_eyes = [eye for eye in worst_eyes if eye.issue != EyeIssue.EYE_NORMAL] + return bad_eyes + + def determine_issue(self, measurements: List[EyeData]): + # 优先级: 小,大,上,下, + # 假设不会有偏光导致眼图小的情况,一定是先解决眼图小和大的问题,再解决眼图上和下的问题 + worst_eye = min(measurements, key=lambda x: x.quality_score) + if worst_eye.issue in [EyeIssue.EYE_NORMAL, EyeIssue.EYE_TOO_LARGE]: + # 如果是眼最小的是正常的或者偏大的, 那就返回最大的 + worst_eye = max(measurements, key=lambda x: x.quality_score) + + return worst_eye + + def get_worst_eye_diagram_per_lane(self, exp_id: int, retimer_phys_index: int, + phys_lane: int, attempts: int = 3) -> EyeData: + + measurements: List[EyeData] = [] + + for attempt in range(1, attempts + 1): + try: + logger.info(f"Attempt {attempt}/{attempts} for RTMR{retimer_phys_index} Lane{phys_lane}") + eye_data = self.get_eye_diagram_per_lane(exp_id, retimer_phys_index, phys_lane) + + if eye_data.rt_phys_index != -1: + measurements.append(eye_data) + logger.info(f"Measurement {attempt}: {eye_data}") + + # 如果发现眼图有问题,直接返回,不再继续查询 + if eye_data.issue != EyeIssue.EYE_NORMAL: + logger.info(f"Eye issue detected: {eye_data.issue.name}, returning immediately") + return eye_data + else: + logger.warning(f"Invalid measurement in attempt {attempt}") + + except Exception as e: + logger.error(f"Attempt {attempt} failed: {str(e)}") + + # 如果所有尝试都是正常眼图,则进行综合判断 + if not measurements: + logger.error(f"No valid measurements obtained after {attempts} attempts") + return EyeData() # 返回默认的空数据 + + # 找出质量最差的眼图 + worst_eye = self.determine_issue(measurements) + logger.warning(f"Worst eye diagram for RTMR{retimer_phys_index} Lane{phys_lane}: " + f"Quality score: {worst_eye.quality_score:.2f}") + + return worst_eye + def get_eye_diagram_per_lane(self, exp_id: int, retimer_phys_index: int, + phys_lane: int, attempts: int = 3) -> EyeData: + measurements: List[EyeData] = [] + + for attempt in range(1, attempts + 1): + try: + logger.info(f"Attempt {attempt}/{attempts} for RTMR{retimer_phys_index} Lane{phys_lane}") + eye_data = self.get_eye_diagram_per_lane_1_count(exp_id, retimer_phys_index, phys_lane) + + if eye_data.rt_phys_index != -1: + measurements.append(eye_data) + logger.info(f"Measurement {attempt}: {eye_data}") + + # 如果发现眼图有问题,直接返回,不再继续查询 + if eye_data.issue != EyeIssue.EYE_NORMAL: + logger.info(f"Eye issue detected: {eye_data.issue.name}, returning immediately") + return eye_data + else: + logger.warning(f"Invalid measurement in attempt {attempt}") + + except Exception as e: + logger.error(f"Attempt {attempt} failed: {str(e)}") + + # 如果所有尝试都是正常眼图,则进行综合判断 + if not measurements: + logger.error(f"No valid measurements obtained after {attempts} attempts") + return EyeData() # 返回默认的空数据 + + # 找出质量最差的眼图 + worst_eye = self.determine_issue(measurements) + logger.warning(f"Worst eye diagram for RTMR{retimer_phys_index} Lane{phys_lane}: " + f"Quality score: {worst_eye.quality_score:.2f}") + + return worst_eye + + def get_eye_diagram_per_lane_1_count(self, exp_id: int, retimer_phys_index: int, phys_lane: int) -> EyeData: + eye_data = EyeData() + cmd = f"rtmr {retimer_phys_index} eye 0 a {phys_lane}" + try: + raw_output = self.local_bmc.CmdVendorCommand(exp_id, cmd) + # logging.info(f'raw_output: {raw_output}') + if raw_output == '': + logger.error(f"No data returned from command: {cmd}") + return eye_data + eye_datas = self.parse_eye_data(raw_output) + return eye_datas[0] + except Exception as e: + logger.error(f"Error executing command '{cmd}': {e}") + return eye_data + def get_eye_diagram(self, exp_id: int, retimer_index: int) -> List[EyeData]: + cmd = f"rtmr {retimer_index} eye 0 a" + try: + raw_output = self.local_bmc.CmdVendorCommand(exp_id, cmd) + if not raw_output: + logger.error(f"No data returned from command: {cmd}") + return [] + return self.parse_eye_data(raw_output) + except Exception as e: + logger.error(f"Error executing command '{cmd}': {e}") + return [] + def parse_eye_data(self, text: str) -> List[EyeData]: -//http.cpp -// smbus_http.cpp -#include "smbus_http.h" -#include -#include -#include + pattern = ( + r"RTMR(\d+)\s+EYE_A(\d+):\s*" + r"\((-?\d+\.?\d*),\s*(-?\d+\.?\d*)\)\s*mV\s*\|\s*" + r"\((-?\d+\.?\d*),\s*(-?\d+\.?\d*)\)\s*UI" + ) + regex = re.compile(pattern) + results = [] -#include "lt_config.h" -#define CONFIG_ENABLE_EXP_REMAP (config_.exp_remap) -#define CONFIG_ENABLE_TRACE (config_.trace_payload) + for line_num, line in enumerate(text.strip().splitlines(), 1): + line = line.strip() + if not line or "error" in line.lower(): + continue -static std::map static_exp_map_ = {{2, 1}, {4, 2}, {6, 3}, {8, 4}}; -static std::map headers_ = { - {"Content-Type", "application/json"}, - {"Accept", "application/json"} -}; -#define PRINT(...) do {\ - if (CONFIG_ENABLE_TRACE) printf(__VA_ARGS__); \ -} while (0) + match = regex.search(line) # 使用 search 允许前后有额外字符 + if not match: + logger.debug(f"Line {line_num}: Skipped (no match) - {line}") + continue -SmbusHttp::SmbusHttp(const struct http_t& config, const std::string& token) : config_(config), token_(token) -{ - http_helper_ = std::make_unique(config_.user, config_.pswd); - - if (CONFIG_ENABLE_EXP_REMAP) exp_map_ = static_exp_map_; -} + try: + rtmr_prefix = match.group(1) + eye_index = int(match.group(2)) + down = float(match.group(3)) + top = float(match.group(4)) + left = float(match.group(5)) + right = float(match.group(6)) -std::vector SmbusHttp::string_to_bytes(const std::string& hexString) -{ - std::vector bytes; - if (hexString.length() % 2 != 0) return bytes; // 返回空vector,表示无效输入 - - bytes.reserve(hexString.length() / 2); - for (size_t i = 0; i < hexString.length(); i += 2) - { - std::string byteString = hexString.substr(i, 2); - uint8_t byte = static_cast(std::stoi(byteString, nullptr, 16)); - bytes.push_back(byte); - } - return bytes; -} - -std::string SmbusHttp::build_user_pwd() -{ - std::ostringstream json; - json << "{"; - json << "\"UserName\":\"" << config_.user << "\","; - json << "\"Password\":\"" << config_.pswd << "\""; - json << "}"; - return json.str(); -} - -std::string SmbusHttp::build_get_register_json(int exp_id, int cmd_id, int read_len) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id << ","; - json << "\"CmdId\":" << cmd_id << ","; - json << "\"ReadLen\":" << read_len; - json << "}"; - return json.str(); -} - -std::string SmbusHttp::build_set_register_json(int exp_id, int cmd_id, int write_len, const std::string& value) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id << ","; - json << "\"CmdId\":" << cmd_id << ","; - json << "\"WriteLen\":" << write_len << ","; - json << "\"Value\":\"" << value << "\""; - json << "}"; - return json.str(); -} - -std::string SmbusHttp::build_get_base_info_json(int exp_id, int addr, int read_len) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id << ","; - json << "\"Addr\":" << addr << ","; - json << "\"ReadLen\":" << read_len; - json << "}"; - return json.str(); -} - -int SmbusHttp::get_register(int exp_id, int cmd_id, int read_len, std::vector& bytes) -{ - exp_id = remap(exp_id); - - std::string url = config_.host + config_.get_register; - std::string json_payload = build_get_register_json(exp_id, cmd_id, read_len); + # 计算真实 retimer_index 和 lane_index + rt_phys_index = int(f"{rtmr_prefix}") + phys_lane_index = eye_index + if eye_index < 8: + rt_index = int(f"{rtmr_prefix}1") + lane_index = eye_index + else: + rt_index = int(f"{rtmr_prefix}2") + lane_index = eye_index - 8 - PRINT("get_register:\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); + eye_data = EyeData( + rt_phys_index=rt_phys_index, + phys_lane_index=phys_lane_index, + rt_index=rt_index, + lane_index=lane_index, + down=down, + top=top, + left=left, + right=right + ) + eye_data.determine_issue() # 初始化问题类型 + results.append(eye_data) - PRINT("Response: %s\n", response_str.c_str()); + except Exception as e: + logger.error(f"Failed to parse line {line_num}: {line} | Error: {e}") + continue - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - bytes = string_to_bytes(result.second); - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} + logger.info(f"Parsed {len(results)} valid eye diagrams.") + return results -int SmbusHttp::set_register(int exp_id, int cmd_id, int write_len, const std::vector& value) -{ - exp_id = remap(exp_id); - std::string url = config_.host + config_.set_register; - - std::string hexString(value.size() * 2, '0'); - for (size_t i = 0; i < value.size(); ++i) - sprintf(&hexString[i * 2], "%02X", value[i]); - std::string json_payload = build_set_register_json(exp_id, cmd_id, write_len, hexString); - PRINT("set_register\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); - - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} -int SmbusHttp::get_fru(int exp_id, uint8_t addr, int num, std::vector& bytes) -{ - exp_id = remap(exp_id); - std::string url = config_.host + config_.get_fru; - std::string json_payload = build_get_base_info_json(exp_id, static_cast(addr), num); - PRINT("get_fru\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - bytes = string_to_bytes(result.second); - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} - -SmbusHttpBiren::SmbusHttpBiren(const struct http_t& config, const std::string& token) : SmbusHttp(config, token) -{ - if (0 == token_.length()) - { - token_ = get_token(); - }\ - if (token_.length()) http_helper_->set_token(token_); -} - -int SmbusHttpBiren::lock(int exp_id, int seconds) -{ - exp_id = remap(exp_id); - std::string url = config_.host + config_.access_lock; - - std::string json_payload = build_lock(exp_id); - - PRINT("lock\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); - - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} -int SmbusHttpBiren::unlock(int exp_id) -{ - exp_id = remap(exp_id); - std::string url = config_.host + config_.access_unlock; - std::string json_payload = build_unlock(exp_id); - PRINT("unlock\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); - - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} - -std::string SmbusHttpBiren::build_lock(int exp_id) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id; - json << "}"; - return json.str(); -} -std::string SmbusHttpBiren::build_unlock(int exp_id) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id; - json << "}"; - return json.str(); -} - -std::pair SmbusHttpBiren::parse_response(const std::string& response) -{ - int CompletionCode = -1; - json j = json::parse(response); - if (j.contains("Status")) - { - if ("OK" == j["Status"].get()) - { - if (j.contains("Data") && j["Data"].contains("Value")) - return std::make_pair(0, j["Data"]["Value"].get()); - return std::make_pair(0, ""); - } - } - return std::make_pair(CompletionCode, "Invalid Response"); -} - -std::pair SmbusHttpBiren::parse_token(const std::string& response) -{ - int CompletionCode = -1; - json j = json::parse(response); - if (j.contains("X-Auth-Token")) - { - CompletionCode = 0; - return std::make_pair(CompletionCode, j["X-Auth-Token"].get()); - } - return std::make_pair(CompletionCode, "Invalid Response"); -} - -int SmbusHttpBiren::remap(int exp_id) -{ - if (!token_.empty() && !exp_map_.empty()) - { - auto it = exp_map_.find(exp_id); - if (it != exp_map_.end()) return it->second; - } - return exp_id; -} - -std::string SmbusHttpBiren::get_token() -{ - std::string url = config_.host + config_.get_token; - std::string json_payload = build_user_pwd(); - - PRINT("get_token:\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true, true); - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_token(response_str); - if (result.first == 0) - { - return result.second; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return ""; -} -int SmbusHttpH3C::lock(int exp_id, int seconds) -{ - exp_id = remap(exp_id); - std::string url = config_.host + config_.access_lock; - std::string json_payload = build_lock(exp_id, seconds); - PRINT("lock\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); - } - } - - return -1; -} -int SmbusHttpH3C::unlock(int exp_id) -{ - exp_id = remap(exp_id); +# ocsdiag.py +def process_host(host, reg_table_file, exp_list, slot_list, lane_list, cmd, param, reg_wt_value, sl_file): + logging.info(f'-----------process: {host}') - std::string url = config_.host + config_.access_unlock; - std::string json_payload = build_unlock(exp_id); + for exp_id in exp_list: + exp_id = int(exp_id) - PRINT("unlock\nurl: %s\n", url.c_str()); - PRINT("Payload: %s\n", json_payload.c_str()); - - std::string response_str = http_helper_->post(url, json_payload, headers_, true); + exp_util = SmbusHttpUtil(f"https://{host}", "root", "0penBmc", host) - PRINT("Response: %s\n", response_str.c_str()); - - if (!response_str.empty()) - { - auto result = parse_response(response_str); - if (result.first == 0) - { - return 0; - } - else - { - std::cerr << __PRETTY_FUNCTION__ << " failed, Code: " << result.first << ", Desc:" << result.second << std::endl; - exit(1); + 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', } - } - - return -1; -} - -std::pair SmbusHttpH3C::parse_response(const std::string& response) -{ - int CompletionCode = -1; - json j = json::parse(response); - if (j.contains("Oem") && j["Oem"].contains("Public") && j["Oem"]["Public"].contains("CompletionCode") && j["Oem"]["Public"].contains("Description")) - { - CompletionCode = j["Oem"]["Public"]["CompletionCode"].get(); - if (CompletionCode == 0 && j.contains("Data") && j["Data"].contains("RegisterValue")) - return std::make_pair(CompletionCode, j["Data"]["RegisterValue"].get()); - return std::make_pair(CompletionCode, j["Oem"]["Public"]["Description"].get()); - } - return std::make_pair(CompletionCode, "Invalid Response"); -} -std::string SmbusHttpH3C::build_lock(int exp_id, int seconds) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id << ","; - json << "\"timeout\":" << seconds; - json << "}"; - return json.str(); -} - -std::string SmbusHttpH3C::build_unlock(int exp_id) -{ - std::ostringstream json; - json << "{"; - json << "\"ExpanderId\":" << exp_id << ","; - json << "}"; - return json.str(); -} - -//http.h - -// smbus_http_util.h -#ifndef SMBUS_HTTP_H -#define SMBUS_HTTP_H - -#include -#include -#include -#include -#include -#include "http_helper.h" -#include "lt_config.h" - -class SmbusHttp -{ - -public: - SmbusHttp(const struct http_t& config, const std::string& token); - - virtual int lock(int exp_id, int seconds) { return 0; } - virtual int unlock(int exp_id) { return 0; } - virtual std::string get_token() { return ""; } - virtual int get_register(int exp_id, int cmd_id, int read_len, std::vector& bytes); - virtual int set_register(int exp_id, int cmd_id, int write_len, const std::vector& value); - virtual int get_fru(int exp_id, uint8_t addr, int num, std::vector& bytes); - -protected: - struct http_t config_; - std::unique_ptr http_helper_; - - virtual int remap(int exp_id) { return exp_id; } - std::map exp_map_; - - // 手动构建JSON字符串 - std::vector string_to_bytes(const std::string& hexString); - std::string build_user_pwd(); - std::string build_get_register_json(int exp_id, int cmd_id, int read_len); - std::string build_set_register_json(int exp_id, int cmd_id, int write_len, const std::string& value); - std::string build_get_base_info_json(int exp_id, int addr, int read_len); - - // 解析响应中的Status和Data字段 - virtual std::pair parse_response(const std::string& response) = 0; - std::string token_; -}; - -class SmbusHttpBiren : public SmbusHttp -{ -public: - SmbusHttpBiren(const struct http_t& config, const std::string& token); - int lock(int exp_id, int seconds) override; - int unlock(int exp_id) override; - std::string get_token() override; -protected: - int remap(int exp_id) override; - std::pair parse_token(const std::string& response); - std::pair parse_response(const std::string& response); - std::string build_lock(int exp_id); - std::string build_unlock(int exp_id); -}; - -class SmbusHttpH3C : public SmbusHttp -{ -public: - SmbusHttpH3C(const struct http_t& config, const std::string& token) : SmbusHttp(config, token) {} - int lock(int exp_id, int seconds) override; - int unlock(int exp_id) override; -protected: - std::string build_lock(int exp_id, int seconds); - std::string build_unlock(int exp_id); - std::pair parse_response(const std::string& response); -}; - -#endif // SMBUS_HTTP_UTIL_H \ No newline at end of file + + reg_table_file = f'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}') + + 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_bmc = None + remote_exp_util = exp_util + remote_host = host + remote_reg_access_tool = reg_access_tool + if cmd in ['ibias-auto-tune', 'eq-auto-tune'] and param != '': + logging.info(f'------- remote_host: {param}') + remote_host = param + 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']: + slot_list = [0,1,2,3,4,5,6,7] +