From 08d58a9d92c6a0be073e40b81b6d3961ff4ec729 Mon Sep 17 00:00:00 2001 From: Nikhil Kanchi Date: Thu, 16 Jul 2026 22:13:16 +0000 Subject: [PATCH 1/2] C-CMIS PM support Signed-off-by: Nikhil Kanchi --- .../sonic_xcvr/api/public/c_cmis.py | 221 +++++++++++++++- .../sonic_xcvr/api/public/cmis.py | 11 +- .../sonic_xcvr/api/xcvr_api.py | 40 +++ .../sonic_xcvr/fields/consts.py | 146 +++++++++++ .../sonic_xcvr/mem_maps/public/cmis/c_cmis.py | 4 + .../mem_maps/public/cmis/pages/__init__.py | 6 +- .../mem_maps/public/cmis/pages/page01.py | 1 + .../mem_maps/public/cmis/pages/page35.py | 15 ++ .../mem_maps/public/cmis/pages/page3a.py | 31 +++ .../mem_maps/public/cmis/pages/page42.py | 134 ++++++++++ tests/sonic_xcvr/test_ccmis.py | 241 ++++++++++++++---- tests/sonic_xcvr/test_cmis.py | 20 ++ 12 files changed, 799 insertions(+), 71 deletions(-) create mode 100644 sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page3a.py create mode 100644 sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page42.py diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index 65acbc274..3ebc43e8c 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -7,6 +7,7 @@ from .cmis import CmisApi, CMIS_VDM_KEY_TO_DB_PREFIX_KEY_MAP, CMIS_XCVR_INFO_DEFAULT_DICT import time import copy +from ...utils.cache import read_only_cached_api_return C_CMIS_DELTA_VDM_KEY_TO_DB_PREFIX_KEY_MAP = { 'Modulator Bias X/I [%]' : 'biasxi', @@ -187,21 +188,74 @@ def set_tx_power(self, tx_power): time.sleep(1) return status + @read_only_cached_api_return + def _is_rx_clockrec_pm_implemented(self): + ''' + Returns True if the Page 42h clock recovery loop PM advertisement bit is + set. + ''' + return (self.is_coherent_module() and not self.is_flat_memory() + and bool(self.xcvr_eeprom.read(consts.RX_CLOCK_REC_IMPL))) + + @read_only_cached_api_return + def _is_rx_lg_sopmd_pm_implemented(self): + ''' + Returns True if the Page 42h low-granularity SOPMD PM advertisement bit is + set. + ''' + return (self.is_coherent_module() and not self.is_flat_memory() + and bool(self.xcvr_eeprom.read(consts.RX_LG_SOPMD_IMPL))) + + @read_only_cached_api_return + def _is_rx_snr_margin_pm_implemented(self): + ''' + Returns True if the Page 42h SNR margin PM advertisement bit is set. + ''' + return (self.is_coherent_module() and not self.is_flat_memory() + and bool(self.xcvr_eeprom.read(consts.RX_SNR_MARGIN_IMPL))) + + @read_only_cached_api_return + def _is_rx_qfactor_pm_implemented(self): + ''' + Returns True if the Page 42h Q-factor PM advertisement bit is set. + ''' + return (self.is_coherent_module() and not self.is_flat_memory() + and bool(self.xcvr_eeprom.read(consts.RX_QFACTOR_IMPL))) + + @read_only_cached_api_return + def _is_rx_qmargin_pm_implemented(self): + ''' + Returns True if the Page 42h Q-margin PM advertisement bit is set. + ''' + return (self.is_coherent_module() and not self.is_flat_memory() + and bool(self.xcvr_eeprom.read(consts.RX_QMARGIN_IMPL))) + def get_pm_all(self): ''' - This function returns the PMs reported in Page 34h and 35h in OIF C-CMIS document - CD: unit in ps/nm - DGD: unit in ps - SOPMD: unit in ps^2 - PDL: unit in dB - OSNR: unit in dB - ESNR: unit in dB - CFO: unit in MHz - TXpower:unit in dBm - RXpower:unit in dBm - RX sig power: unit in dBm - SOPROC: unit in krad/s - MER: unit in dB + This function returns the PMs reported in Page 34h, 35h and 3Ah in the + OIF C-CMIS document. + CD: unit in ps/nm + DGD: unit in ps + SOPMD: unit in ps^2 + PDL: unit in dB + OSNR: unit in dB + ESNR: unit in dB + CFO: unit in MHz + EVM: unit in % + TXpower: unit in dBm + RXpower: unit in dBm + RX sig power: unit in dBm + SOPROC: unit in krad/s + MER: unit in dB + Clock recovery: unit in % + Low-granularity SOPMD: unit in ps^2 + SNR margin: unit in dB + Q-factor: unit in dB + Q-margin: unit in dB + + The raw Page 34h RX FEC counters (rx_bits_pm, rx_corr_bits_pm, rx_frames_pm, + ...) are also included alongside the derived preFEC_* ratios computed from + them, so downstream consumers can publish the raw counter values. ''' PM_dict = dict() @@ -220,6 +274,12 @@ def get_pm_all(self): PM_dict['preFEC_BER_avg'] = 1.0 PM_dict['preFEC_BER_min'] = 1.0 PM_dict['preFEC_BER_max'] = 1.0 + # Raw page 34h RX FEC bit counters (published to the counters table). + PM_dict['rx_bits_pm'] = rx_bits_pm + PM_dict['rx_bits_subint_pm'] = rx_bits_subint_pm + PM_dict['rx_corr_bits_pm'] = rx_corr_bits_pm + PM_dict['rx_min_corr_bits_subint_pm'] = rx_min_corr_bits_subint_pm + PM_dict['rx_max_corr_bits_subint_pm'] = rx_max_corr_bits_subint_pm rx_frames_pm = self.xcvr_eeprom.read(consts.RX_FRAMES_PM) rx_frames_subint_pm = self.xcvr_eeprom.read(consts.RX_FRAMES_SUB_INTERVAL_PM) rx_frames_uncorr_err_pm = self.xcvr_eeprom.read(consts.RX_FRAMES_UNCORR_ERR_PM) @@ -235,6 +295,12 @@ def get_pm_all(self): PM_dict['preFEC_uncorr_frame_ratio_avg'] = 0 PM_dict['preFEC_uncorr_frame_ratio_min'] = 0 PM_dict['preFEC_uncorr_frame_ratio_max'] = 0 + # Raw page 34h RX FEC frame counters (published to the counters table). + PM_dict['rx_frames_pm'] = rx_frames_pm + PM_dict['rx_frames_subint_pm'] = rx_frames_subint_pm + PM_dict['rx_frames_uncorr_err_pm'] = rx_frames_uncorr_err_pm + PM_dict['rx_min_frames_uncorr_err_subint_pm'] = rx_min_frames_uncorr_err_subint_pm + PM_dict['rx_max_frames_uncorr_err_subint_pm'] = rx_max_frames_uncorr_err_subint_pm PM_dict['rx_cd_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_CD_PM) PM_dict['rx_cd_min'] = self.xcvr_eeprom.read(consts.RX_MIN_CD_PM) PM_dict['rx_cd_max'] = self.xcvr_eeprom.read(consts.RX_MAX_CD_PM) @@ -286,6 +352,48 @@ def get_pm_all(self): PM_dict['rx_mer_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_MER_PM) PM_dict['rx_mer_min'] = self.xcvr_eeprom.read(consts.RX_MIN_MER_PM) PM_dict['rx_mer_max'] = self.xcvr_eeprom.read(consts.RX_MAX_MER_PM) + + # Page 35h monitors added after the original release. These are gated on + # their Page 42h advertisement bit: the keys are only added when the field + # is advertised as implemented, and omitted otherwise. + if self._is_rx_clockrec_pm_implemented(): + PM_dict['rx_clockrec_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_CLOCK_REC_PM) + PM_dict['rx_clockrec_min'] = self.xcvr_eeprom.read(consts.RX_MIN_CLOCK_REC_PM) + PM_dict['rx_clockrec_max'] = self.xcvr_eeprom.read(consts.RX_MAX_CLOCK_REC_PM) + + if self._is_rx_lg_sopmd_pm_implemented(): + PM_dict['rx_lg_sopmd_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_LG_SOPMD_PM) + PM_dict['rx_lg_sopmd_min'] = self.xcvr_eeprom.read(consts.RX_MIN_LG_SOPMD_PM) + PM_dict['rx_lg_sopmd_max'] = self.xcvr_eeprom.read(consts.RX_MAX_LG_SOPMD_PM) + + if self._is_rx_snr_margin_pm_implemented(): + PM_dict['rx_snr_margin_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_SNR_MARGIN_PM) + PM_dict['rx_snr_margin_min'] = self.xcvr_eeprom.read(consts.RX_MIN_SNR_MARGIN_PM) + PM_dict['rx_snr_margin_max'] = self.xcvr_eeprom.read(consts.RX_MAX_SNR_MARGIN_PM) + + if self._is_rx_qfactor_pm_implemented(): + PM_dict['rx_qfactor_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_QFACTOR_PM) + PM_dict['rx_qfactor_min'] = self.xcvr_eeprom.read(consts.RX_MIN_QFACTOR_PM) + PM_dict['rx_qfactor_max'] = self.xcvr_eeprom.read(consts.RX_MAX_PM_QFACTOR) + + if self._is_rx_qmargin_pm_implemented(): + PM_dict['rx_qmargin_avg'] = self.xcvr_eeprom.read(consts.RX_AVG_QMARGIN_PM) + PM_dict['rx_qmargin_min'] = self.xcvr_eeprom.read(consts.RX_MIN_QMARGIN_PM) + PM_dict['rx_qmargin_max'] = self.xcvr_eeprom.read(consts.RX_MAX_QMARGIN_PM) + + # Page 3Ah - data path host interface PM + PM_dict['tx_bits_pm'] = self.xcvr_eeprom.read(consts.TX_BITS_PM) + PM_dict['tx_bits_subint_pm'] = self.xcvr_eeprom.read(consts.TX_BITS_SUB_INTERVAL_PM) + PM_dict['tx_corr_bits_pm'] = self.xcvr_eeprom.read(consts.TX_CORR_BITS_PM) + PM_dict['tx_min_corr_bits_subint_pm'] = self.xcvr_eeprom.read(consts.TX_MIN_CORR_BITS_SUB_INTERVAL_PM) + PM_dict['tx_max_corr_bits_subint_pm'] = self.xcvr_eeprom.read(consts.TX_MAX_CORR_BITS_SUB_INTERVAL_PM) + PM_dict['tx_frames_pm'] = self.xcvr_eeprom.read(consts.TX_FRAMES_PM) + PM_dict['tx_frames_subint_pm'] = self.xcvr_eeprom.read(consts.TX_FRAMES_SUB_INTERVAL_PM) + PM_dict['tx_frames_uncorr_err_pm'] = self.xcvr_eeprom.read(consts.TX_FRAMES_UNCORR_ERR_PM) + PM_dict['tx_min_frames_uncorr_err_subint_pm'] = self.xcvr_eeprom.read(consts.TX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM) + PM_dict['tx_max_frames_uncorr_err_subint_pm'] = self.xcvr_eeprom.read(consts.TX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM) + PM_dict['tx_corrected_frames_pm'] = self.xcvr_eeprom.read(consts.TX_CORRECTED_FRAMES_PM) + PM_dict['tx_corrected_frames_subint_pm'] = self.xcvr_eeprom.read(consts.TX_CORRECTED_FRAMES_SUB_INTERVAL_PM) return PM_dict def _get_xcvr_info_default_dict(self): @@ -418,6 +526,16 @@ def get_transceiver_pm(self): ======================================================================== key = TRANSCEIVER_PM|ifname ; information of PM on port ; field = value + rx_bits_pm = INTEGER ; media rx bits over PM interval + rx_bits_subint_pm = INTEGER ; media rx bits over sub-interval + rx_corr_bits_pm = INTEGER ; media rx corrected bits over PM interval + rx_min_corr_bits_subint_pm = INTEGER ; media rx min corrected bits over sub-interval + rx_max_corr_bits_subint_pm = INTEGER ; media rx max corrected bits over sub-interval + rx_frames_pm = INTEGER ; media rx frames over PM interval + rx_frames_subint_pm = INTEGER ; media rx frames over sub-interval + rx_frames_uncorr_err_pm = INTEGER ; media rx uncorrectable frames over PM interval + rx_min_frames_uncorr_err_subint_pm = INTEGER ; media rx min uncorrectable frames over sub-interval + rx_max_frames_uncorr_err_subint_pm = INTEGER ; media rx max uncorrectable frames over sub-interval prefec_ber_avg = FLOAT ; prefec ber avg prefec_ber_min = FLOAT ; prefec ber min prefec_ber_max = FLOAT ; prefec ber max @@ -457,16 +575,64 @@ def get_transceiver_pm(self): rx_sig_power_avg = FLOAT ; rx signal power avg rx_sig_power_min = FLOAT ; rx signal power min rx_sig_power_max = FLOAT ; rx signal power max + evm_avg = FLOAT ; error vector magnitude avg + evm_min = FLOAT ; error vector magnitude min + evm_max = FLOAT ; error vector magnitude max + clockrec_avg = FLOAT ; clock recovery loop monitor avg + clockrec_min = FLOAT ; clock recovery loop monitor min + clockrec_max = FLOAT ; clock recovery loop monitor max + lg_sopmd_avg = FLOAT ; low granularity SOPMD avg + lg_sopmd_min = FLOAT ; low granularity SOPMD min + lg_sopmd_max = FLOAT ; low granularity SOPMD max + snr_margin_avg = FLOAT ; SNR margin avg + snr_margin_min = FLOAT ; SNR margin min + snr_margin_max = FLOAT ; SNR margin max + qfactor_avg = FLOAT ; Q factor avg + qfactor_min = FLOAT ; Q factor min + qfactor_max = FLOAT ; Q factor max + qmargin_avg = FLOAT ; Q margin avg + qmargin_min = FLOAT ; Q margin min + qmargin_max = FLOAT ; Q margin max + tx_bits_pm = INTEGER ; host tx bits over PM interval + tx_bits_subint_pm = INTEGER ; host tx bits over sub-interval + tx_corr_bits_pm = INTEGER ; host tx corrected bits over PM interval + tx_min_corr_bits_subint_pm = INTEGER ; host tx min corrected bits over sub-interval + tx_max_corr_bits_subint_pm = INTEGER ; host tx max corrected bits over sub-interval + tx_frames_pm = INTEGER ; host tx frames over PM interval + tx_frames_subint_pm = INTEGER ; host tx frames over sub-interval + tx_frames_uncorr_err_pm = INTEGER ; host tx uncorrectable frames over PM interval + tx_min_frames_uncorr_err_subint_pm = INTEGER ; host tx min uncorrectable frames over sub-interval + tx_max_frames_uncorr_err_subint_pm = INTEGER ; host tx max uncorrectable frames over sub-interval + tx_corrected_frames_pm = INTEGER ; host tx corrected frames over PM interval + tx_corrected_frames_subint_pm = INTEGER ; host tx corrected frames over sub-interval ======================================================================== + + Note: the later Page 35h additions (clock recovery, LG-SOPMD, SNR margin, + Q-factor, Q-margin) are gated on their Page 42h advertisement bit, so they + surface here as None on modules that don't advertise them, preserving + backwards compatibility. """ trans_pm = dict() PM_dict = self.get_pm_all() + # Page 34h media lane FEC PM counters + trans_pm['rx_bits_pm'] = PM_dict['rx_bits_pm'] + trans_pm['rx_bits_subint_pm'] = PM_dict['rx_bits_subint_pm'] + trans_pm['rx_corr_bits_pm'] = PM_dict['rx_corr_bits_pm'] + trans_pm['rx_min_corr_bits_subint_pm'] = PM_dict['rx_min_corr_bits_subint_pm'] + trans_pm['rx_max_corr_bits_subint_pm'] = PM_dict['rx_max_corr_bits_subint_pm'] + trans_pm['rx_frames_pm'] = PM_dict['rx_frames_pm'] + trans_pm['rx_frames_subint_pm'] = PM_dict['rx_frames_subint_pm'] + trans_pm['rx_frames_uncorr_err_pm'] = PM_dict['rx_frames_uncorr_err_pm'] + trans_pm['rx_min_frames_uncorr_err_subint_pm'] = PM_dict['rx_min_frames_uncorr_err_subint_pm'] + trans_pm['rx_max_frames_uncorr_err_subint_pm'] = PM_dict['rx_max_frames_uncorr_err_subint_pm'] + # Page 34h media lane FEC PM - derived values trans_pm['prefec_ber_avg'] = PM_dict['preFEC_BER_avg'] trans_pm['prefec_ber_min'] = PM_dict['preFEC_BER_min'] trans_pm['prefec_ber_max'] = PM_dict['preFEC_BER_max'] trans_pm['uncorr_frames_avg'] = PM_dict['preFEC_uncorr_frame_ratio_avg'] trans_pm['uncorr_frames_min'] = PM_dict['preFEC_uncorr_frame_ratio_min'] trans_pm['uncorr_frames_max'] = PM_dict['preFEC_uncorr_frame_ratio_max'] + # Page 35h media lane link PM trans_pm['cd_avg'] = PM_dict['rx_cd_avg'] trans_pm['cd_min'] = PM_dict['rx_cd_min'] trans_pm['cd_max'] = PM_dict['rx_cd_max'] @@ -503,4 +669,33 @@ def get_transceiver_pm(self): trans_pm['rx_sig_power_avg'] = PM_dict['rx_sigpwr_avg'] trans_pm['rx_sig_power_min'] = PM_dict['rx_sigpwr_min'] trans_pm['rx_sig_power_max'] = PM_dict['rx_sigpwr_max'] + # Page 35h media lane link PM - advertisement gated values + trans_pm['clockrec_avg'] = PM_dict.get('rx_clockrec_avg') + trans_pm['clockrec_min'] = PM_dict.get('rx_clockrec_min') + trans_pm['clockrec_max'] = PM_dict.get('rx_clockrec_max') + trans_pm['lg_sopmd_avg'] = PM_dict.get('rx_lg_sopmd_avg') + trans_pm['lg_sopmd_min'] = PM_dict.get('rx_lg_sopmd_min') + trans_pm['lg_sopmd_max'] = PM_dict.get('rx_lg_sopmd_max') + trans_pm['snr_margin_avg'] = PM_dict.get('rx_snr_margin_avg') + trans_pm['snr_margin_min'] = PM_dict.get('rx_snr_margin_min') + trans_pm['snr_margin_max'] = PM_dict.get('rx_snr_margin_max') + trans_pm['qfactor_avg'] = PM_dict.get('rx_qfactor_avg') + trans_pm['qfactor_min'] = PM_dict.get('rx_qfactor_min') + trans_pm['qfactor_max'] = PM_dict.get('rx_qfactor_max') + trans_pm['qmargin_avg'] = PM_dict.get('rx_qmargin_avg') + trans_pm['qmargin_min'] = PM_dict.get('rx_qmargin_min') + trans_pm['qmargin_max'] = PM_dict.get('rx_qmargin_max') + # Page 3Ah host interface PM counters + trans_pm['tx_bits_pm'] = PM_dict['tx_bits_pm'] + trans_pm['tx_bits_subint_pm'] = PM_dict['tx_bits_subint_pm'] + trans_pm['tx_corr_bits_pm'] = PM_dict['tx_corr_bits_pm'] + trans_pm['tx_min_corr_bits_subint_pm'] = PM_dict['tx_min_corr_bits_subint_pm'] + trans_pm['tx_max_corr_bits_subint_pm'] = PM_dict['tx_max_corr_bits_subint_pm'] + trans_pm['tx_frames_pm'] = PM_dict['tx_frames_pm'] + trans_pm['tx_frames_subint_pm'] = PM_dict['tx_frames_subint_pm'] + trans_pm['tx_frames_uncorr_err_pm'] = PM_dict['tx_frames_uncorr_err_pm'] + trans_pm['tx_min_frames_uncorr_err_subint_pm'] = PM_dict['tx_min_frames_uncorr_err_subint_pm'] + trans_pm['tx_max_frames_uncorr_err_subint_pm'] = PM_dict['tx_max_frames_uncorr_err_subint_pm'] + trans_pm['tx_corrected_frames_pm'] = PM_dict['tx_corrected_frames_pm'] + trans_pm['tx_corrected_frames_subint_pm'] = PM_dict['tx_corrected_frames_subint_pm'] return trans_pm diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 416d5b581..280958f00 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -1050,8 +1050,17 @@ def get_module_media_interface(self): @read_only_cached_api_return def is_coherent_module(self): ''' - Returns True if the module follow C-CMIS spec, False otherwise + Returns True if the module follows C-CMIS spec, False otherwise. + + CMIS 5.3 introduced an C-CMIS advertisement bit on Page 01h + (byte 142, bit 4). For modules complying with CMIS 5.3 or + later, that bit is used for checking C-CMIS support. ''' + cmis_major = self.xcvr_eeprom.read(consts.CMIS_MAJOR_REVISION) + cmis_minor = self.xcvr_eeprom.read(consts.CMIS_MINOR_REVISION) + if cmis_major is not None and cmis_minor is not None and (cmis_major, cmis_minor) >= (5, 3): + return not self.is_flat_memory() and bool(self.xcvr_eeprom.read(consts.COHERENT_PAGES_SUPPORT_ADVT_FIELD)) + mintf = self.get_module_media_interface() return False if 'ZR' not in mintf else True diff --git a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py index 51af230fe..93878f001 100644 --- a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py +++ b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py @@ -307,6 +307,46 @@ def get_transceiver_pm(self): rx_sig_power_avg = FLOAT ; rx signal power avg rx_sig_power_min = FLOAT ; rx signal power min rx_sig_power_max = FLOAT ; rx signal power max + evm_avg = FLOAT ; error vector magnitude avg + evm_min = FLOAT ; error vector magnitude min + evm_max = FLOAT ; error vector magnitude max + clockrec_avg = FLOAT ; clock recovery loop monitor avg + clockrec_min = FLOAT ; clock recovery loop monitor min + clockrec_max = FLOAT ; clock recovery loop monitor max + lg_sopmd_avg = FLOAT ; low granularity SOPMD avg + lg_sopmd_min = FLOAT ; low granularity SOPMD min + lg_sopmd_max = FLOAT ; low granularity SOPMD max + snr_margin_avg = FLOAT ; SNR margin avg + snr_margin_min = FLOAT ; SNR margin min + snr_margin_max = FLOAT ; SNR margin max + qfactor_avg = FLOAT ; Q factor avg + qfactor_min = FLOAT ; Q factor min + qfactor_max = FLOAT ; Q factor max + qmargin_avg = FLOAT ; Q margin avg + qmargin_min = FLOAT ; Q margin min + qmargin_max = FLOAT ; Q margin max + tx_bits_pm = INTEGER ; host tx bits over PM interval + tx_bits_subint_pm = INTEGER ; host tx bits over sub-interval + tx_corr_bits_pm = INTEGER ; host tx corrected bits over PM interval + tx_min_corr_bits_subint_pm = INTEGER ; host tx min corrected bits over sub-interval + tx_max_corr_bits_subint_pm = INTEGER ; host tx max corrected bits over sub-interval + tx_frames_pm = INTEGER ; host tx frames over PM interval + tx_frames_subint_pm = INTEGER ; host tx frames over sub-interval + tx_frames_uncorr_err_pm = INTEGER ; host tx uncorrectable frames over PM interval + tx_min_frames_uncorr_err_subint_pm = INTEGER ; host tx min uncorrectable frames over sub-interval + tx_max_frames_uncorr_err_subint_pm = INTEGER ; host tx max uncorrectable frames over sub-interval + tx_corrected_frames_pm = INTEGER ; host tx corrected frames over PM interval + tx_corrected_frames_subint_pm = INTEGER ; host tx corrected frames over sub-interval + rx_bits_pm = INTEGER ; media rx bits over PM interval + rx_bits_subint_pm = INTEGER ; media rx bits over sub-interval + rx_corr_bits_pm = INTEGER ; media rx corrected bits over PM interval + rx_min_corr_bits_subint_pm = INTEGER ; media rx min corrected bits over sub-interval + rx_max_corr_bits_subint_pm = INTEGER ; media rx max corrected bits over sub-interval + rx_frames_pm = INTEGER ; media rx frames over PM interval + rx_frames_subint_pm = INTEGER ; media rx frames over sub-interval + rx_frames_uncorr_err_pm = INTEGER ; media rx uncorrectable frames over PM interval + rx_min_frames_uncorr_err_subint_pm = INTEGER ; media rx min uncorrectable frames over sub-interval + rx_max_frames_uncorr_err_subint_pm = INTEGER ; media rx max uncorrectable frames over sub-interval If there is an issue with reading the xcvr, None should be returned. """ diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index c4881da34..8854f1eb9 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -292,6 +292,7 @@ FLAGS_ADVT_FIELD = "Supported Flags Advertisement" PAGE_SUPPORT_ADVT_FIELD = "Supported Pages Advertisement" DIAG_PAGE_SUPPORT_ADVT_FIELD = "Supported Diagnostic Pages Advertisement" +COHERENT_PAGES_SUPPORT_ADVT_FIELD = "Supported Coherent Pages Advertisement" TX_FLAGS_ADVT_FIELD = "Supported TX Flags Advertisement" RX_FLAGS_ADVT_FIELD = "Supported RX Flags Advertisement" LANE_MON_ADVT_FIELD = "Supported Lane Monitor Advertisement" @@ -484,6 +485,151 @@ RX_AVG_MER_PM = "rxAvgMerPm" RX_MIN_MER_PM = "rxMinMerPm" RX_MAX_MER_PM = "rxMaxMerPm" +RX_AVG_CLOCK_REC_PM = "rxAvgClockRecPm" +RX_MIN_CLOCK_REC_PM = "rxMinClockRecPm" +RX_MAX_CLOCK_REC_PM = "rxMaxClockRecPm" +RX_AVG_LG_SOPMD_PM = "rxAvgLGSopmdPm" +RX_MIN_LG_SOPMD_PM = "rxMinLGSopmdPm" +RX_MAX_LG_SOPMD_PM = "rxMaxLGSopmdPm" +RX_AVG_SNR_MARGIN_PM = "rxAvgSNRMarginPm" +RX_MIN_SNR_MARGIN_PM = "rxMinSNRMarginPm" +RX_MAX_SNR_MARGIN_PM = "rxMaxSNRMarginPm" +RX_AVG_QFACTOR_PM = "rxAvgQFactorPm" +RX_MIN_QFACTOR_PM = "rxMinQFactorPm" +RX_MAX_PM_QFACTOR = "rxMaxPmQFactor" +RX_AVG_QMARGIN_PM = "rxAvgQMarginPm" +RX_MIN_QMARGIN_PM = "rxMinQMarginPm" +RX_MAX_QMARGIN_PM = "rxMaxQMarginPm" + +## Data Path Host Interface Performance Monitoring +DATA_PATH_HOST_IF_PM = "Data Path Host Interface Performance Monitoring" +TX_BITS_PM = "txBitsPm" +TX_BITS_SUB_INTERVAL_PM = "txBitsSubIntPm" +TX_CORR_BITS_PM = "txCorrBitsPm" +TX_MIN_CORR_BITS_SUB_INTERVAL_PM = "txMinCorrBitsSubIntPm" +TX_MAX_CORR_BITS_SUB_INTERVAL_PM = "txMaxCorrBitsSubIntPm" +TX_FRAMES_PM = "txFramesPm" +TX_FRAMES_SUB_INTERVAL_PM = "txFramesSubIntPm" +TX_FRAMES_UNCORR_ERR_PM = "txFramesUncorrErrPm" +TX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM = "txMinFramesUncorrErrSubintPm" +TX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM = "txMaxFramesUncorrErrSubintPm" +TX_CORRECTED_FRAMES_PM = "txCorrectedFramesPm" +TX_CORRECTED_FRAMES_SUB_INTERVAL_PM = "txCorrectedFramesSubintPm" + +## Performance Monitoring Advertisement (Page 42h) +PM_ADVT_FIELD = "PmAdvertisement" + +# Byte 128 (page 34h RX bit-count fields) +RX_BITS_PM_IMPL = "rxBitsPmImpl" +RX_BITS_SUB_INTERVAL_PM_IMPL = "rxBitsSubIntPmImpl" +RX_CORR_BITS_PM_IMPL = "rxCorrBitsPmImpl" +RX_MIN_CORR_BITS_SUB_INTERVAL_PM_IMPL = "rxMinCorrBitsSubIntPmImpl" +RX_MAX_CORR_BITS_SUB_INTERVAL_PM_IMPL = "rxMaxCorrBitsSubIntPmImpl" + +# Byte 129 (page 34h RX frame-count fields) +RX_FRAMES_PM_IMPL = "rxFramesPmImpl" +RX_FRAMES_SUB_INTERVAL_PM_IMPL = "rxFramesSubIntPmImpl" +RX_FRAMES_UNCORR_ERR_PM_IMPL = "rxFramesUncorrErrPmImpl" +RX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM_IMPL = "rxMinFramesUncorrErrSubintPmImpl" +RX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM_IMPL = "rxMaxFramesUncorrErrSubintPmImpl" + +# Byte 130 (CD/DGD) +RX_CD_IMPL = "rxCdImpl" +RX_AVG_CD_PM_IMPL = "rxAvgCdPmImpl" +RX_MIN_CD_PM_IMPL = "rxMinCdPmImpl" +RX_MAX_CD_PM_IMPL = "rxMaxCdPmImpl" +RX_DGD_IMPL = "rxDgdImpl" +RX_AVG_DGD_PM_IMPL = "rxAvgDgdPmImpl" +RX_MIN_DGD_PM_IMPL = "rxMinDgdPmImpl" +RX_MAX_DGD_PM_IMPL = "rxMaxDgdPmImpl" + +# Byte 131 (LG-SOPMD/PDL) +RX_LG_SOPMD_IMPL = "rxLGSopmdImpl" +RX_AVG_LG_SOPMD_PM_IMPL = "rxAvgLGSopmdPmImpl" +RX_MIN_LG_SOPMD_PM_IMPL = "rxMinLGSopmdPmImpl" +RX_MAX_LG_SOPMD_PM_IMPL = "rxMaxLGSopmdPmImpl" +RX_PDL_IMPL = "rxPdlImpl" +RX_AVG_PDL_PM_IMPL = "rxAvgPdlPmImpl" +RX_MIN_PDL_PM_IMPL = "rxMinPdlPmImpl" +RX_MAX_PDL_PM_IMPL = "rxMaxPdlPmImpl" + +# Byte 132 (OSNR/eSNR) +RX_OSNR_IMPL = "rxOsnrImpl" +RX_AVG_OSNR_PM_IMPL = "rxAvgOsnrPmImpl" +RX_MIN_OSNR_PM_IMPL = "rxMinOsnrPmImpl" +RX_MAX_OSNR_PM_IMPL = "rxMaxOsnrPmImpl" +RX_ESNR_IMPL = "rxEsnrImpl" +RX_AVG_ESNR_PM_IMPL = "rxAvgEsnrPmImpl" +RX_MIN_ESNR_PM_IMPL = "rxMinEsnrPmImpl" +RX_MAX_ESNR_PM_IMPL = "rxMaxEsnrPmImpl" + +# Byte 133 (CFO/EVM) +RX_CFO_IMPL = "rxCfoImpl" +RX_AVG_CFO_PM_IMPL = "rxAvgCfoPmImpl" +RX_MIN_CFO_PM_IMPL = "rxMinCfoPmImpl" +RX_MAX_CFO_PM_IMPL = "rxMaxCfoPmImpl" +RX_EVM_IMPL = "rxEvmModemImpl" +RX_AVG_EVM_PM_IMPL = "rxAvgEvmModemPmImpl" +RX_MIN_EVM_PM_IMPL = "rxMinEvmModemPmImpl" +RX_MAX_EVM_PM_IMPL = "rxMaxEvmModemPmImpl" + +# Byte 134 (SOPROC/TX power) +RX_SOPROC_IMPL = "rxSopcrImpl" +RX_AVG_SOPROC_PM_IMPL = "rxAvgSopcrPmImpl" +RX_MIN_SOPROC_PM_IMPL = "rxMinSopcrPmImpl" +RX_MAX_SOPROC_PM_IMPL = "rxMaxSopcrPmImpl" +TX_POWER_IMPL = "txPowerImpl" +TX_AVG_POWER_PM_IMPL = "txAvgPowerPmImpl" +TX_MIN_POWER_PM_IMPL = "txMinPowerPmImpl" +TX_MAX_POWER_PM_IMPL = "txMaxPowerPmImpl" + +# Byte 135 (RX power/RX sig power) +RX_POWER_IMPL = "rxPowerImpl" +RX_AVG_POWER_PM_IMPL = "rxAvgPowerPmImpl" +RX_MIN_POWER_PM_IMPL = "rxMinPowerPmImpl" +RX_MAX_POWER_PM_IMPL = "rxMaxPowerPmImpl" +RX_SIG_POWER_IMPL = "rxSigPowerImpl" +RX_AVG_SIG_POWER_PM_IMPL = "rxAvgSigPowerPmImpl" +RX_MIN_SIG_POWER_PM_IMPL = "rxMinSigPowerPmImpl" +RX_MAX_SIG_POWER_PM_IMPL = "rxMaxSigPowerPmImpl" + +# Byte 136 (page 33h alarm-latch gates - consts only, not wired into get_pm_all) +RX_MEDIA_FED_PM_IMPL = "rxMediaFedPmImpl" +RX_MEDIA_FDD_PM_IMPL = "rxMediaFddPmImpl" + +# Byte 137 (page 3Bh alarm-latch gates - consts only, not wired into get_pm_all) +TX_HOST_FED_PM_IMPL = "txHostFedPmImpl" +TX_HOST_FDD_PM_IMPL = "txHostFddPmImpl" + +# Byte 138 (clock recovery/HG-SOPMD) +RX_CLOCK_REC_IMPL = "rxClockRecPmImpl" +RX_AVG_CLOCK_REC_PM_IMPL = "rxAvgClockRecPmImpl" +RX_MIN_CLOCK_REC_PM_IMPL = "rxMinClockRecPmImpl" +RX_MAX_CLOCK_REC_PM_IMPL = "rxMaxClockRecPmImpl" +RX_HG_SOPMD_IMPL = "rxHGSopmdImpl" +RX_AVG_HG_SOPMD_PM_IMPL = "rxAvgHGSopmdPmImpl" +RX_MIN_HG_SOPMD_PM_IMPL = "rxMinHGSopmdPmImpl" +RX_MAX_HG_SOPMD_PM_IMPL = "rxMaxHGSopmdPmImpl" + +# Byte 139 (SNR margin/MER) +RX_SNR_MARGIN_IMPL = "rxSNRMarginPmImpl" +RX_AVG_SNR_MARGIN_PM_IMPL = "rxAvgSNRMarginPmImpl" +RX_MIN_SNR_MARGIN_PM_IMPL = "rxMinSNRMarginPmImpl" +RX_MAX_SNR_MARGIN_PM_IMPL = "rxMaxSNRMarginPmImpl" +RX_MER_IMPL = "rxMERPmImpl" +RX_AVG_MER_PM_IMPL = "rxAvgMERPmImpl" +RX_MIN_MER_PM_IMPL = "rxMinMERPmImpl" +RX_MAX_MER_PM_IMPL = "rxMaxMERPmImpl" + +# Byte 140 (Q-factor/Q-margin) +RX_QFACTOR_IMPL = "rxQFactorPmImpl" +RX_AVG_QFACTOR_PM_IMPL = "rxAvgQFactorPmImpl" +RX_MIN_QFACTOR_PM_IMPL = "rxMinQFactorPmImpl" +RX_MAX_QFACTOR_PM_IMPL = "rxMaxQFactorPmImpl" +RX_QMARGIN_IMPL = "rxQMarginPmImpl" +RX_AVG_QMARGIN_PM_IMPL = "rxAvgQMarginPmImpl" +RX_MIN_QMARGIN_PM_IMPL = "rxMinQMarginPmImpl" +RX_MAX_QMARGIN_PM_IMPL = "rxMaxQMarginPmImpl" # CDB TRANS_CDB_FIELD = "TransceiverCdb" diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/c_cmis.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/c_cmis.py index 3cd84caf0..8b9e68177 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/c_cmis.py @@ -9,6 +9,8 @@ CCmisModuleConfigSupportPage, CCmisMediaLaneFecPmPage, CCmisMediaLaneLinkPmPage, + CCmisDataPathHostIfPmPage, + CCmisPmAdvertisementPage ) @@ -21,4 +23,6 @@ def __init__(self, codes, bank=0): CCmisModuleConfigSupportPage(codes, bank=bank), # 0x04 CCmisMediaLaneFecPmPage(codes, bank=bank), # 0x34 CCmisMediaLaneLinkPmPage(codes, bank=bank), # 0x35 + CCmisDataPathHostIfPmPage(codes, bank=bank), # 0x3A + CCmisPmAdvertisementPage(codes, bank=bank), # 0x42 ) diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/__init__.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/__init__.py index 63c7f716b..739063af2 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/__init__.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/__init__.py @@ -31,6 +31,8 @@ from .page2f import CmisVdmAdvertisingCtrlPage from .page34 import CCmisMediaLaneFecPmPage from .page35 import CCmisMediaLaneLinkPmPage +from .page3a import CCmisDataPathHostIfPmPage +from .page42 import CCmisPmAdvertisementPage from .page9f import CmisCdbMessagePage from .page9f_cdb import CdbLplMessagePage @@ -60,6 +62,8 @@ 'CmisVdmAdvertisingCtrlPage', 'CCmisMediaLaneFecPmPage', 'CCmisMediaLaneLinkPmPage', + 'CCmisDataPathHostIfPmPage', + 'CCmisPmAdvertisementPage', 'CmisCdbMessagePage', - 'CdbLplMessagePage', + 'CdbLplMessagePage' ] diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page01.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page01.py index 76fa40725..f0082c19d 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page01.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page01.py @@ -85,6 +85,7 @@ def __init__(self, codes, page=ADVERTISING_PAGE): NumberRegField(consts.PAGE_SUPPORT_ADVT_FIELD, self.getaddr(142), RegBitField(consts.VDM_SUPPORTED, 6), RegBitField(consts.DIAG_PAGE_SUPPORT_ADVT_FIELD, 5), + RegBitField(consts.COHERENT_PAGES_SUPPORT_ADVT_FIELD, 4), ), CodeRegField(consts.BANKS_SUPPORTED_FIELD, self.getaddr(142), codes.MAX_BANKS_SUPPORTED, *(RegBitField("Bit%d" % bit, bit) for bit in range(0, 2)) diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page35.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page35.py index 7abc0f32e..5f2fa042f 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page35.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page35.py @@ -56,4 +56,19 @@ def __init__(self, codes, bank=0, page=0x35): NumberRegField(consts.RX_AVG_MER_PM, self.getaddr(206), format=">H", size=2, scale=10.0), NumberRegField(consts.RX_MIN_MER_PM, self.getaddr(208), format=">H", size=2, scale=10.0), NumberRegField(consts.RX_MAX_MER_PM, self.getaddr(210), format=">H", size=2, scale=10.0), + NumberRegField(consts.RX_AVG_CLOCK_REC_PM, self.getaddr(212), format=">h", size=2, scale=327.67), + NumberRegField(consts.RX_MIN_CLOCK_REC_PM, self.getaddr(214), format=">h", size=2, scale=327.67), + NumberRegField(consts.RX_MAX_CLOCK_REC_PM, self.getaddr(216), format=">h", size=2, scale=327.67), + NumberRegField(consts.RX_AVG_LG_SOPMD_PM, self.getaddr(218), format=">H", size=2), + NumberRegField(consts.RX_MIN_LG_SOPMD_PM, self.getaddr(220), format=">H", size=2), + NumberRegField(consts.RX_MAX_LG_SOPMD_PM, self.getaddr(222), format=">H", size=2), + NumberRegField(consts.RX_AVG_SNR_MARGIN_PM, self.getaddr(224), format=">h", size=2, scale=10.0), + NumberRegField(consts.RX_MIN_SNR_MARGIN_PM, self.getaddr(226), format=">h", size=2, scale=10.0), + NumberRegField(consts.RX_MAX_SNR_MARGIN_PM, self.getaddr(228), format=">h", size=2, scale=10.0), + NumberRegField(consts.RX_AVG_QFACTOR_PM, self.getaddr(230), format=">H", size=2, scale=10.0), + NumberRegField(consts.RX_MIN_QFACTOR_PM, self.getaddr(232), format=">H", size=2, scale=10.0), + NumberRegField(consts.RX_MAX_PM_QFACTOR, self.getaddr(234), format=">H", size=2, scale=10.0), + NumberRegField(consts.RX_AVG_QMARGIN_PM, self.getaddr(236), format=">h", size=2, scale=10.0), + NumberRegField(consts.RX_MIN_QMARGIN_PM, self.getaddr(238), format=">h", size=2, scale=10.0), + NumberRegField(consts.RX_MAX_QMARGIN_PM, self.getaddr(240), format=">h", size=2, scale=10.0), ] diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page3a.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page3a.py new file mode 100644 index 000000000..95ff2e73b --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page3a.py @@ -0,0 +1,31 @@ +""" + page3a.py + + C-CMIS Page 3Ah - Data Path Host Interface Performance Monitors. +""" + +from .page import CmisPage +from .....fields.xcvr_field import NumberRegField +from .....fields import consts + + +class CCmisDataPathHostIfPmPage(CmisPage): + """C-CMIS Page 3Ah: data path host-interface performance-monitor counters.""" + + def __init__(self, codes, bank=0, page=0x3A): + super().__init__(codes, page=page, bank=bank) + + self.fields[consts.DATA_PATH_HOST_IF_PM] = [ + NumberRegField(consts.TX_BITS_PM, self.getaddr(128), format=">Q", size=8), + NumberRegField(consts.TX_BITS_SUB_INTERVAL_PM, self.getaddr(136), format=">Q", size=8), + NumberRegField(consts.TX_CORR_BITS_PM, self.getaddr(144), format=">Q", size=8), + NumberRegField(consts.TX_MIN_CORR_BITS_SUB_INTERVAL_PM, self.getaddr(152), format=">Q", size=8), + NumberRegField(consts.TX_MAX_CORR_BITS_SUB_INTERVAL_PM, self.getaddr(160), format=">Q", size=8), + NumberRegField(consts.TX_FRAMES_PM, self.getaddr(168), format=">I", size=4), + NumberRegField(consts.TX_FRAMES_SUB_INTERVAL_PM, self.getaddr(172), format=">I", size=4), + NumberRegField(consts.TX_FRAMES_UNCORR_ERR_PM, self.getaddr(176), format=">I", size=4), + NumberRegField(consts.TX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM, self.getaddr(180), format=">I", size=4), + NumberRegField(consts.TX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM, self.getaddr(184), format=">I", size=4), + NumberRegField(consts.TX_CORRECTED_FRAMES_PM, self.getaddr(188), format=">I", size=4), + NumberRegField(consts.TX_CORRECTED_FRAMES_SUB_INTERVAL_PM, self.getaddr(192), format=">I", size=4), + ] diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page42.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page42.py new file mode 100644 index 000000000..0d4ea00e6 --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis/pages/page42.py @@ -0,0 +1,134 @@ +""" + page42.py + + C-CMIS Page 42h - Performance Monitoring Advertisement. + + Advertises which Page 34h/35h performance monitors are implemented by the + module. All fields are global (non-banked) capability bits. +""" + +from .page import CmisPage +from .....fields.xcvr_field import NumberRegField, RegBitField +from .....fields import consts + + +class CCmisPmAdvertisementPage(CmisPage): + """C-CMIS Page 42h: performance-monitoring "Impl" advertisement bits.""" + + def __init__(self, codes, bank=0, page=0x42): + super().__init__(codes, page=page, bank=bank) + + self.fields[consts.PM_ADVT_FIELD] = [ + NumberRegField("PmAdvtByte128", self.getaddr(128), + RegBitField(consts.RX_MAX_CORR_BITS_SUB_INTERVAL_PM_IMPL, 4), + RegBitField(consts.RX_MIN_CORR_BITS_SUB_INTERVAL_PM_IMPL, 3), + RegBitField(consts.RX_CORR_BITS_PM_IMPL, 2), + RegBitField(consts.RX_BITS_SUB_INTERVAL_PM_IMPL, 1), + RegBitField(consts.RX_BITS_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte129", self.getaddr(129), + RegBitField(consts.RX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM_IMPL, 4), + RegBitField(consts.RX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM_IMPL, 3), + RegBitField(consts.RX_FRAMES_UNCORR_ERR_PM_IMPL, 2), + RegBitField(consts.RX_FRAMES_SUB_INTERVAL_PM_IMPL, 1), + RegBitField(consts.RX_FRAMES_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte130", self.getaddr(130), + RegBitField(consts.RX_CD_IMPL, 7), + RegBitField(consts.RX_AVG_CD_PM_IMPL, 6), + RegBitField(consts.RX_MIN_CD_PM_IMPL, 5), + RegBitField(consts.RX_MAX_CD_PM_IMPL, 4), + RegBitField(consts.RX_DGD_IMPL, 3), + RegBitField(consts.RX_AVG_DGD_PM_IMPL, 2), + RegBitField(consts.RX_MIN_DGD_PM_IMPL, 1), + RegBitField(consts.RX_MAX_DGD_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte131", self.getaddr(131), + RegBitField(consts.RX_LG_SOPMD_IMPL, 7), + RegBitField(consts.RX_AVG_LG_SOPMD_PM_IMPL, 6), + RegBitField(consts.RX_MIN_LG_SOPMD_PM_IMPL, 5), + RegBitField(consts.RX_MAX_LG_SOPMD_PM_IMPL, 4), + RegBitField(consts.RX_PDL_IMPL, 3), + RegBitField(consts.RX_AVG_PDL_PM_IMPL, 2), + RegBitField(consts.RX_MIN_PDL_PM_IMPL, 1), + RegBitField(consts.RX_MAX_PDL_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte132", self.getaddr(132), + RegBitField(consts.RX_OSNR_IMPL, 7), + RegBitField(consts.RX_AVG_OSNR_PM_IMPL, 6), + RegBitField(consts.RX_MIN_OSNR_PM_IMPL, 5), + RegBitField(consts.RX_MAX_OSNR_PM_IMPL, 4), + RegBitField(consts.RX_ESNR_IMPL, 3), + RegBitField(consts.RX_AVG_ESNR_PM_IMPL, 2), + RegBitField(consts.RX_MIN_ESNR_PM_IMPL, 1), + RegBitField(consts.RX_MAX_ESNR_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte133", self.getaddr(133), + RegBitField(consts.RX_CFO_IMPL, 7), + RegBitField(consts.RX_AVG_CFO_PM_IMPL, 6), + RegBitField(consts.RX_MIN_CFO_PM_IMPL, 5), + RegBitField(consts.RX_MAX_CFO_PM_IMPL, 4), + RegBitField(consts.RX_EVM_IMPL, 3), + RegBitField(consts.RX_AVG_EVM_PM_IMPL, 2), + RegBitField(consts.RX_MIN_EVM_PM_IMPL, 1), + RegBitField(consts.RX_MAX_EVM_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte134", self.getaddr(134), + RegBitField(consts.RX_SOPROC_IMPL, 7), + RegBitField(consts.RX_AVG_SOPROC_PM_IMPL, 6), + RegBitField(consts.RX_MIN_SOPROC_PM_IMPL, 5), + RegBitField(consts.RX_MAX_SOPROC_PM_IMPL, 4), + RegBitField(consts.TX_POWER_IMPL, 3), + RegBitField(consts.TX_AVG_POWER_PM_IMPL, 2), + RegBitField(consts.TX_MIN_POWER_PM_IMPL, 1), + RegBitField(consts.TX_MAX_POWER_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte135", self.getaddr(135), + RegBitField(consts.RX_POWER_IMPL, 7), + RegBitField(consts.RX_AVG_POWER_PM_IMPL, 6), + RegBitField(consts.RX_MIN_POWER_PM_IMPL, 5), + RegBitField(consts.RX_MAX_POWER_PM_IMPL, 4), + RegBitField(consts.RX_SIG_POWER_IMPL, 3), + RegBitField(consts.RX_AVG_SIG_POWER_PM_IMPL, 2), + RegBitField(consts.RX_MIN_SIG_POWER_PM_IMPL, 1), + RegBitField(consts.RX_MAX_SIG_POWER_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte136", self.getaddr(136), + RegBitField(consts.RX_MEDIA_FED_PM_IMPL, 1), + RegBitField(consts.RX_MEDIA_FDD_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte137", self.getaddr(137), + RegBitField(consts.TX_HOST_FED_PM_IMPL, 1), + RegBitField(consts.TX_HOST_FDD_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte138", self.getaddr(138), + RegBitField(consts.RX_CLOCK_REC_IMPL, 7), + RegBitField(consts.RX_AVG_CLOCK_REC_PM_IMPL, 6), + RegBitField(consts.RX_MIN_CLOCK_REC_PM_IMPL, 5), + RegBitField(consts.RX_MAX_CLOCK_REC_PM_IMPL, 4), + RegBitField(consts.RX_HG_SOPMD_IMPL, 3), + RegBitField(consts.RX_AVG_HG_SOPMD_PM_IMPL, 2), + RegBitField(consts.RX_MIN_HG_SOPMD_PM_IMPL, 1), + RegBitField(consts.RX_MAX_HG_SOPMD_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte139", self.getaddr(139), + RegBitField(consts.RX_SNR_MARGIN_IMPL, 7), + RegBitField(consts.RX_AVG_SNR_MARGIN_PM_IMPL, 6), + RegBitField(consts.RX_MIN_SNR_MARGIN_PM_IMPL, 5), + RegBitField(consts.RX_MAX_SNR_MARGIN_PM_IMPL, 4), + RegBitField(consts.RX_MER_IMPL, 3), + RegBitField(consts.RX_AVG_MER_PM_IMPL, 2), + RegBitField(consts.RX_MIN_MER_PM_IMPL, 1), + RegBitField(consts.RX_MAX_MER_PM_IMPL, 0), + ), + NumberRegField("PmAdvtByte140", self.getaddr(140), + RegBitField(consts.RX_QFACTOR_IMPL, 7), + RegBitField(consts.RX_AVG_QFACTOR_PM_IMPL, 6), + RegBitField(consts.RX_MIN_QFACTOR_PM_IMPL, 5), + RegBitField(consts.RX_MAX_QFACTOR_PM_IMPL, 4), + RegBitField(consts.RX_QMARGIN_IMPL, 3), + RegBitField(consts.RX_AVG_QMARGIN_PM_IMPL, 2), + RegBitField(consts.RX_MIN_QMARGIN_PM_IMPL, 1), + RegBitField(consts.RX_MAX_QMARGIN_PM_IMPL, 0), + ), + ] diff --git a/tests/sonic_xcvr/test_ccmis.py b/tests/sonic_xcvr/test_ccmis.py index 53a996c65..b2b0512f8 100644 --- a/tests/sonic_xcvr/test_ccmis.py +++ b/tests/sonic_xcvr/test_ccmis.py @@ -5,6 +5,101 @@ from sonic_platform_base.sonic_xcvr.mem_maps.public.cmis.c_cmis import CCmisMemMap from sonic_platform_base.sonic_xcvr.xcvr_eeprom import XcvrEeprom from sonic_platform_base.sonic_xcvr.codes.public.cmis import CmisCodes +from sonic_platform_base.sonic_xcvr.fields import consts + + +# Page 42h "Impl" advertisement bits for the *newly added* Page 35h monitors. +# The original Page 34h/35h fields are always read (no advertisement gating). +_PM_IMPL_CONSTS = [ + consts.RX_CLOCK_REC_IMPL, consts.RX_LG_SOPMD_IMPL, consts.RX_SNR_MARGIN_IMPL, + consts.RX_QFACTOR_IMPL, consts.RX_QMARGIN_IMPL, +] + +# Page 3Ah host-interface counters are ungated: always present in get_pm_all(). +_PAGE_3AH_EXPECTED = { + 'tx_bits_pm': 2000000, 'tx_bits_subint_pm': 20000, 'tx_corr_bits_pm': 2000, + 'tx_min_corr_bits_subint_pm': 16, 'tx_max_corr_bits_subint_pm': 24, + 'tx_frames_pm': 20000, 'tx_frames_subint_pm': 200, 'tx_frames_uncorr_err_pm': 0, + 'tx_min_frames_uncorr_err_subint_pm': 0, 'tx_max_frames_uncorr_err_subint_pm': 0, + 'tx_corrected_frames_pm': 5, 'tx_corrected_frames_subint_pm': 1, +} + +# Raw Page 34h RX FEC counters: always present (ungated, mirror the ratio reads). +_PAGE_34_RAW_EXPECTED = { + 'rx_bits_pm': 1000000, 'rx_bits_subint_pm': 10000, 'rx_corr_bits_pm': 1000, + 'rx_min_corr_bits_subint_pm': 8, 'rx_max_corr_bits_subint_pm': 12, + 'rx_frames_pm': 10000, 'rx_frames_subint_pm': 100, 'rx_frames_uncorr_err_pm': 0, + 'rx_min_frames_uncorr_err_subint_pm': 0, 'rx_max_frames_uncorr_err_subint_pm': 0, +} + +# Original Page 34h/35h keys: always emitted by get_pm_all() (no advertisement gating). +_PAGE_34_35_OLD_EXPECTED = { + 'preFEC_BER_avg': 0.001, 'preFEC_BER_min': 0.0008, 'preFEC_BER_max': 0.0012, + 'preFEC_uncorr_frame_ratio_avg': 0, 'preFEC_uncorr_frame_ratio_min': 0, 'preFEC_uncorr_frame_ratio_max': 0, + 'rx_cd_avg': 1400, 'rx_cd_min': 1300, 'rx_cd_max': 1500, + 'rx_dgd_avg': 7.0, 'rx_dgd_min': 5.5, 'rx_dgd_max': 9.2, + 'rx_sopmd_avg': 40, 'rx_sopmd_min': 20, 'rx_sopmd_max': 60, + 'rx_pdl_avg': 1.0, 'rx_pdl_min': 0.8, 'rx_pdl_max': 1.2, + 'rx_osnr_avg': 28, 'rx_osnr_min': 26, 'rx_osnr_max': 30, + 'rx_esnr_avg': 17, 'rx_esnr_min': 15, 'rx_esnr_max': 18, + 'rx_cfo_avg': 200, 'rx_cfo_min': 150, 'rx_cfo_max': 250, + 'rx_evm_avg': 15, 'rx_evm_min': 13, 'rx_evm_max': 18, + 'tx_power_avg': -10, 'tx_power_min': -9.5, 'tx_power_max': -10.5, + 'rx_power_avg': -8, 'rx_power_min': -7, 'rx_power_max': -9, + 'rx_sigpwr_avg': -8, 'rx_sigpwr_min': -7, 'rx_sigpwr_max': -9, + 'rx_soproc_avg': 5, 'rx_soproc_min': 3, 'rx_soproc_max': 8, + 'rx_mer_avg': 0, 'rx_mer_min': 0, 'rx_mer_max': 0, +} + +# Newly added Page 35h keys: only emitted when advertised implemented in Page 42h. +_PAGE_35_NEW_EXPECTED = { + 'rx_clockrec_avg': 1.5, 'rx_clockrec_min': 1.0, 'rx_clockrec_max': 2.0, + 'rx_lg_sopmd_avg': 41, 'rx_lg_sopmd_min': 21, 'rx_lg_sopmd_max': 61, + 'rx_snr_margin_avg': 3.0, 'rx_snr_margin_min': 2.0, 'rx_snr_margin_max': 4.0, + 'rx_qfactor_avg': 10.0, 'rx_qfactor_min': 9.0, 'rx_qfactor_max': 11.0, + 'rx_qmargin_avg': 1.0, 'rx_qmargin_min': 0.5, 'rx_qmargin_max': 1.5, +} + + +def _pm_all_read_map(): + """Const -> mocked xcvr_eeprom.read() return value, with everything implemented.""" + values = { + # Page 34h - media FEC PM raw counters + consts.RX_BITS_PM: 1000000, consts.RX_BITS_SUB_INTERVAL_PM: 10000, + consts.RX_CORR_BITS_PM: 1000, consts.RX_MIN_CORR_BITS_SUB_INTERVAL_PM: 8, + consts.RX_MAX_CORR_BITS_SUB_INTERVAL_PM: 12, + consts.RX_FRAMES_PM: 10000, consts.RX_FRAMES_SUB_INTERVAL_PM: 100, + consts.RX_FRAMES_UNCORR_ERR_PM: 0, consts.RX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM: 0, + consts.RX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM: 0, + # Page 35h - media lane link PM + consts.RX_AVG_CD_PM: 1400, consts.RX_MIN_CD_PM: 1300, consts.RX_MAX_CD_PM: 1500, + consts.RX_AVG_DGD_PM: 7.0, consts.RX_MIN_DGD_PM: 5.5, consts.RX_MAX_DGD_PM: 9.2, + consts.RX_AVG_SOPMD_PM: 40, consts.RX_MIN_SOPMD_PM: 20, consts.RX_MAX_SOPMD_PM: 60, + consts.RX_AVG_PDL_PM: 1.0, consts.RX_MIN_PDL_PM: 0.8, consts.RX_MAX_PDL_PM: 1.2, + consts.RX_AVG_OSNR_PM: 28, consts.RX_MIN_OSNR_PM: 26, consts.RX_MAX_OSNR_PM: 30, + consts.RX_AVG_ESNR_PM: 17, consts.RX_MIN_ESNR_PM: 15, consts.RX_MAX_ESNR_PM: 18, + consts.RX_AVG_CFO_PM: 200, consts.RX_MIN_CFO_PM: 150, consts.RX_MAX_CFO_PM: 250, + consts.RX_AVG_EVM_PM: 15, consts.RX_MIN_EVM_PM: 13, consts.RX_MAX_EVM_PM: 18, + consts.TX_AVG_POWER_PM: -10, consts.TX_MIN_POWER_PM: -9.5, consts.TX_MAX_POWER_PM: -10.5, + consts.RX_AVG_POWER_PM: -8, consts.RX_MIN_POWER_PM: -7, consts.RX_MAX_POWER_PM: -9, + consts.RX_AVG_SIG_POWER_PM: -8, consts.RX_MIN_SIG_POWER_PM: -7, consts.RX_MAX_SIG_POWER_PM: -9, + consts.RX_AVG_SOPROC_PM: 5, consts.RX_MIN_SOPROC_PM: 3, consts.RX_MAX_SOPROC_PM: 8, + consts.RX_AVG_MER_PM: 0, consts.RX_MIN_MER_PM: 0, consts.RX_MAX_MER_PM: 0, + consts.RX_AVG_CLOCK_REC_PM: 1.5, consts.RX_MIN_CLOCK_REC_PM: 1.0, consts.RX_MAX_CLOCK_REC_PM: 2.0, + consts.RX_AVG_LG_SOPMD_PM: 41, consts.RX_MIN_LG_SOPMD_PM: 21, consts.RX_MAX_LG_SOPMD_PM: 61, + consts.RX_AVG_SNR_MARGIN_PM: 3.0, consts.RX_MIN_SNR_MARGIN_PM: 2.0, consts.RX_MAX_SNR_MARGIN_PM: 4.0, + consts.RX_AVG_QFACTOR_PM: 10.0, consts.RX_MIN_QFACTOR_PM: 9.0, consts.RX_MAX_PM_QFACTOR: 11.0, + consts.RX_AVG_QMARGIN_PM: 1.0, consts.RX_MIN_QMARGIN_PM: 0.5, consts.RX_MAX_QMARGIN_PM: 1.5, + # Page 3Ah - data path host interface PM + consts.TX_BITS_PM: 2000000, consts.TX_BITS_SUB_INTERVAL_PM: 20000, consts.TX_CORR_BITS_PM: 2000, + consts.TX_MIN_CORR_BITS_SUB_INTERVAL_PM: 16, consts.TX_MAX_CORR_BITS_SUB_INTERVAL_PM: 24, + consts.TX_FRAMES_PM: 20000, consts.TX_FRAMES_SUB_INTERVAL_PM: 200, consts.TX_FRAMES_UNCORR_ERR_PM: 0, + consts.TX_MIN_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM: 0, consts.TX_MAX_FRAMES_UNCORR_ERR_SUB_INTERVAL_PM: 0, + consts.TX_CORRECTED_FRAMES_PM: 5, consts.TX_CORRECTED_FRAMES_SUB_INTERVAL_PM: 1, + } + for impl in _PM_IMPL_CONSTS: + values[impl] = True + return values class TestCCmis(object): @@ -117,48 +212,67 @@ def test_set_tx_power(self, input_param, mock_response): self.api.get_supported_power_config.return_value = mock_response self.api.set_tx_power(input_param) - @pytest.mark.parametrize("mock_response, expected", [ - ( - [ - 1000000, 10000, 1000, 8, 12, # preFEC_BER - 10000, 100, 0, 0, 0, # uncorr_frame - 1400, 1300, 1500, # CD - 7.0, 5.5, 9.2, # DGD - 40, 20, 60, # SOPMD - 1.0, 0.8, 1.2, # PDL - 28, 26, 30, # OSNR - 17, 15, 18, # ESNR - 200, 150, 250, # CFO - 15, 13, 18, # EVM - -10, -9.5, -10.5, # TX power - -8, -7, -9, # RX total power - -8, -7, -9, # RX channel power - 5, 3, 8, # SOPROc - 0, 0, 0, # MER - ], - { - 'preFEC_BER_avg': 0.001, 'preFEC_BER_min': 0.0008, 'preFEC_BER_max': 0.0012, - 'preFEC_uncorr_frame_ratio_avg': 0, 'preFEC_uncorr_frame_ratio_min': 0, 'preFEC_uncorr_frame_ratio_max': 0, - 'rx_cd_avg': 1400, 'rx_cd_min': 1300, 'rx_cd_max': 1500, - 'rx_dgd_avg': 7.0, 'rx_dgd_min': 5.5, 'rx_dgd_max': 9.2, - 'rx_sopmd_avg': 40, 'rx_sopmd_min': 20, 'rx_sopmd_max': 60, - 'rx_pdl_avg': 1.0, 'rx_pdl_min': 0.8, 'rx_pdl_max': 1.2, - 'rx_osnr_avg': 28, 'rx_osnr_min': 26, 'rx_osnr_max': 30, - 'rx_esnr_avg': 17, 'rx_esnr_min': 15, 'rx_esnr_max': 18, - 'rx_cfo_avg': 200, 'rx_cfo_min': 150, 'rx_cfo_max': 250, - 'rx_evm_avg': 15, 'rx_evm_min': 13, 'rx_evm_max': 18, - 'tx_power_avg': -10, 'tx_power_min': -9.5, 'tx_power_max': -10.5, - 'rx_power_avg': -8, 'rx_power_min': -7, 'rx_power_max': -9, - 'rx_sigpwr_avg': -8, 'rx_sigpwr_min': -7, 'rx_sigpwr_max': -9, - 'rx_soproc_avg': 5, 'rx_soproc_min': 3, 'rx_soproc_max': 8, - 'rx_mer_avg': 0, 'rx_mer_min': 0, 'rx_mer_max': 0 - } - ) - ]) - def test_get_pm_all(self, mock_response, expected): - self.api.xcvr_eeprom.read = MagicMock() - self.api.xcvr_eeprom.read.side_effect = mock_response - result = self.api.get_pm_all() + def test_get_pm_all_all_implemented(self): + values = _pm_all_read_map() + self.api.xcvr_eeprom.read = MagicMock(side_effect=lambda c: values.get(c)) + # Clear any read_only_cached_api_return caches so each test's mock is honored + # (the shared api instance persists across tests). + for attr in [a for a in vars(self.api) if a.endswith('_cache')]: + setattr(self.api, attr, {}) + with patch.object(self.api, 'is_flat_memory', return_value=False), \ + patch.object(self.api, 'is_coherent_module', return_value=True): + result = self.api.get_pm_all() + expected = dict(_PAGE_34_35_OLD_EXPECTED) + expected.update(_PAGE_34_RAW_EXPECTED) + expected.update(_PAGE_35_NEW_EXPECTED) + expected.update(_PAGE_3AH_EXPECTED) + assert result == expected + + def test_get_pm_all_unimplemented_new_fields_omitted(self): + values = _pm_all_read_map() + # Advertise two of the newly added Page 35h monitors as NOT implemented. + values[consts.RX_QFACTOR_IMPL] = False + values[consts.RX_CLOCK_REC_IMPL] = False + self.api.xcvr_eeprom.read = MagicMock(side_effect=lambda c: values.get(c)) + # Clear any read_only_cached_api_return caches so each test's mock is honored + # (the shared api instance persists across tests). + for attr in [a for a in vars(self.api) if a.endswith('_cache')]: + setattr(self.api, attr, {}) + with patch.object(self.api, 'is_flat_memory', return_value=False), \ + patch.object(self.api, 'is_coherent_module', return_value=True): + result = self.api.get_pm_all() + # Unimplemented new-field trios are omitted entirely. + for key in ('rx_qfactor_avg', 'rx_qfactor_min', 'rx_qfactor_max', + 'rx_clockrec_avg', 'rx_clockrec_min', 'rx_clockrec_max'): + assert key not in result + # Other implemented new fields carry their values. + assert result['rx_qmargin_avg'] == 1.0 + assert result['rx_snr_margin_avg'] == 3.0 + # Original Page 34h/35h fields are always present (no advertisement gating). + for key, val in _PAGE_34_35_OLD_EXPECTED.items(): + assert result[key] == val + # Raw Page 34h counters are always present too. + for key, val in _PAGE_34_RAW_EXPECTED.items(): + assert result[key] == val + # Page 3Ah counters are always present regardless of advertisement. + assert result['tx_bits_pm'] == 2000000 + + def test_get_pm_all_flat_memory_keeps_legacy_fields(self): + values = _pm_all_read_map() + self.api.xcvr_eeprom.read = MagicMock(side_effect=lambda c: values.get(c)) + # Clear any read_only_cached_api_return caches so each test's mock is honored + # (the shared api instance persists across tests). + for attr in [a for a in vars(self.api) if a.endswith('_cache')]: + setattr(self.api, attr, {}) + with patch.object(self.api, 'is_flat_memory', return_value=True): + result = self.api.get_pm_all() + # Flat-memory omits every advertisement-gated new Page 35h field... + for key in _PAGE_35_NEW_EXPECTED: + assert key not in result + # ...while the original Page 34h/35h fields and Page 3Ah counters remain. + expected = dict(_PAGE_34_35_OLD_EXPECTED) + expected.update(_PAGE_34_RAW_EXPECTED) + expected.update(_PAGE_3AH_EXPECTED) assert result == expected @pytest.mark.parametrize("mock_response, expected",[ @@ -493,20 +607,10 @@ def test_get_transceiver_status_flags(self, module_faults, tx_fault, tx_los, tx_ @pytest.mark.parametrize("mock_response, expected", [ ( { - 'preFEC_BER_avg': 0.001, 'preFEC_BER_min': 0.0008, 'preFEC_BER_max': 0.0012, - 'preFEC_uncorr_frame_ratio_avg': 0, 'preFEC_uncorr_frame_ratio_min': 0, 'preFEC_uncorr_frame_ratio_max': 0, - 'rx_cd_avg': 1400, 'rx_cd_min': 1300, 'rx_cd_max': 1500, - 'rx_dgd_avg': 7.0, 'rx_dgd_min': 5.5, 'rx_dgd_max': 9.2, - 'rx_sopmd_avg': 40, 'rx_sopmd_min': 20, 'rx_sopmd_max': 60, - 'rx_pdl_avg': 1.0, 'rx_pdl_min': 0.8, 'rx_pdl_max': 1.2, - 'rx_osnr_avg': 28, 'rx_osnr_min': 26, 'rx_osnr_max': 30, - 'rx_esnr_avg': 17, 'rx_esnr_min': 15, 'rx_esnr_max': 18, - 'rx_cfo_avg': 200, 'rx_cfo_min': 150, 'rx_cfo_max': 250, - 'rx_evm_avg': 15, 'rx_evm_min': 13, 'rx_evm_max': 18, - 'tx_power_avg': -10, 'tx_power_min': -9.5, 'tx_power_max': -10.5, - 'rx_power_avg': -8, 'rx_power_min': -7, 'rx_power_max': -9, - 'rx_sigpwr_avg': -8, 'rx_sigpwr_min': -7, 'rx_sigpwr_max': -9, - 'rx_soproc_avg': 5, 'rx_soproc_min': 3, 'rx_soproc_max': 8, + **_PAGE_34_35_OLD_EXPECTED, + **_PAGE_34_RAW_EXPECTED, + **_PAGE_35_NEW_EXPECTED, + **_PAGE_3AH_EXPECTED, }, { 'prefec_ber_avg': 0.001, 'prefec_ber_min': 0.0008, 'prefec_ber_max': 0.0012, @@ -523,6 +627,13 @@ def test_get_transceiver_status_flags(self, module_faults, tx_fault, tx_los, tx_ 'rx_tot_power_avg': -8, 'rx_tot_power_min': -7, 'rx_tot_power_max': -9, 'rx_sig_power_avg': -8, 'rx_sig_power_min': -7, 'rx_sig_power_max': -9, 'soproc_avg': 5, 'soproc_min': 3, 'soproc_max': 8, + 'clockrec_avg': 1.5, 'clockrec_min': 1.0, 'clockrec_max': 2.0, + 'lg_sopmd_avg': 41, 'lg_sopmd_min': 21, 'lg_sopmd_max': 61, + 'snr_margin_avg': 3.0, 'snr_margin_min': 2.0, 'snr_margin_max': 4.0, + 'qfactor_avg': 10.0, 'qfactor_min': 9.0, 'qfactor_max': 11.0, + 'qmargin_avg': 1.0, 'qmargin_min': 0.5, 'qmargin_max': 1.5, + **_PAGE_3AH_EXPECTED, + **_PAGE_34_RAW_EXPECTED, } ) ]) @@ -532,6 +643,24 @@ def test_get_transceiver_pm(self, mock_response, expected): result = self.api.get_transceiver_pm() assert result == expected + def test_get_transceiver_pm_unimplemented_new_fields_are_none(self): + # get_pm_all() omits unimplemented *new* Page 35h keys; get_transceiver_pm() + # must not KeyError and should surface them as None. The original Page + # 34h/35h fields and Page 3Ah counters carry their values. + pm = dict(_PAGE_34_35_OLD_EXPECTED) + pm.update(_PAGE_34_RAW_EXPECTED) + pm.update(_PAGE_3AH_EXPECTED) + self.api.get_pm_all = MagicMock(return_value=pm) + result = self.api.get_transceiver_pm() + # Omitted new fields surface as None. + assert result['qmargin_max'] is None + assert result['clockrec_avg'] is None + assert result['snr_margin_min'] is None + # Original fields and Page 3Ah counters are present. + assert result['cd_avg'] == 1400 + assert result['prefec_ber_avg'] == 0.001 + assert result['tx_bits_pm'] == 2000000 + @pytest.mark.parametrize("mock_response, expected", [ (0, 0), (1, 1), diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index 4b3b7314e..3c016c9b4 100755 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -969,6 +969,26 @@ def test_get_module_media_interface(self, mock_response1, mock_response2, expect def test_is_coherent_module(self, mock_response, expected): self.api.get_module_media_interface = MagicMock() self.api.get_module_media_interface.return_value = mock_response + # Pre-CMIS-5.3 modules don't advertise Coherent Pages support, so + # is_coherent_module() must fall back to the media-interface heuristic. + self.api.xcvr_eeprom.read = MagicMock(return_value=None) + result = self.api.is_coherent_module() + assert result == expected + + @pytest.mark.parametrize("cmis_rev, coherent_pages_supported, flat_mem, expected", [ + ((5, 3), True, False, True), + ((5, 3), False, False, False), + ((5, 3), True, True, False), + ((5, 4), True, False, True), + ]) + def test_is_coherent_module_cmis_5_3_plus(self, cmis_rev, coherent_pages_supported, flat_mem, expected): + values = { + consts.CMIS_MAJOR_REVISION: cmis_rev[0], + consts.CMIS_MINOR_REVISION: cmis_rev[1], + consts.COHERENT_PAGES_SUPPORT_ADVT_FIELD: coherent_pages_supported, + } + self.api.xcvr_eeprom.read = MagicMock(side_effect=lambda c: values.get(c)) + self.api.is_flat_memory = MagicMock(return_value=flat_mem) result = self.api.is_coherent_module() assert result == expected From 516b88f2646f3d5a76cca5e2218ffe58f7382822 Mon Sep 17 00:00:00 2001 From: Nikhil Kanchi Date: Thu, 30 Jul 2026 23:22:07 +0000 Subject: [PATCH 2/2] Consts changes and cmis revision reuse Signed-off-by: Nikhil Kanchi --- sonic_platform_base/sonic_xcvr/api/public/cmis.py | 11 +++++++---- sonic_platform_base/sonic_xcvr/fields/consts.py | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 3ed252653..c81d5a929 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -1058,13 +1058,16 @@ def is_coherent_module(self): ''' Returns True if the module follows C-CMIS spec, False otherwise. - CMIS 5.3 introduced an C-CMIS advertisement bit on Page 01h + CMIS 5.3 introduced a C-CMIS advertisement bit on Page 01h (byte 142, bit 4). For modules complying with CMIS 5.3 or later, that bit is used for checking C-CMIS support. ''' - cmis_major = self.xcvr_eeprom.read(consts.CMIS_MAJOR_REVISION) - cmis_minor = self.xcvr_eeprom.read(consts.CMIS_MINOR_REVISION) - if cmis_major is not None and cmis_minor is not None and (cmis_major, cmis_minor) >= (5, 3): + cmis_rev = self.get_cmis_rev() + try: + cmis_rev_float = float(cmis_rev) if cmis_rev else None + except ValueError: + cmis_rev_float = None + if cmis_rev_float is not None and cmis_rev_float >= 5.3: return not self.is_flat_memory() and bool(self.xcvr_eeprom.read(consts.COHERENT_PAGES_SUPPORT_ADVT_FIELD)) mintf = self.get_module_media_interface() diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index 8854f1eb9..ffc28e0ac 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -574,10 +574,10 @@ RX_MAX_EVM_PM_IMPL = "rxMaxEvmModemPmImpl" # Byte 134 (SOPROC/TX power) -RX_SOPROC_IMPL = "rxSopcrImpl" -RX_AVG_SOPROC_PM_IMPL = "rxAvgSopcrPmImpl" -RX_MIN_SOPROC_PM_IMPL = "rxMinSopcrPmImpl" -RX_MAX_SOPROC_PM_IMPL = "rxMaxSopcrPmImpl" +RX_SOPROC_IMPL = "rxSoprocImpl" +RX_AVG_SOPROC_PM_IMPL = "rxAvgSoprocPmImpl" +RX_MIN_SOPROC_PM_IMPL = "rxMinSoprocPmImpl" +RX_MAX_SOPROC_PM_IMPL = "rxMaxSoprocPmImpl" TX_POWER_IMPL = "txPowerImpl" TX_AVG_POWER_PM_IMPL = "txAvgPowerPmImpl" TX_MIN_POWER_PM_IMPL = "txMinPowerPmImpl"