Skip to content

Commit 95ddbe5

Browse files
Add Support for Arista CMIS Enhanced LPO Debug Registers (#688)
Signed-off-by: Michael Wang <mzwang@arista.com>
1 parent 95e4796 commit 95ddbe5

9 files changed

Lines changed: 986 additions & 0 deletions

File tree

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
'sonic_platform_base.sonic_xcvr.codes.public',
5959
'sonic_platform_base.sonic_xcvr.codes.broadcom',
6060
'sonic_platform_base.sonic_xcvr.utils',
61+
'sonic_platform_base.sonic_xcvr.api.arista',
62+
'sonic_platform_base.sonic_xcvr.mem_maps.arista',
6163
'sonic_platform_base.sonic_xcvr.api.credo',
6264
'sonic_platform_base.sonic_xcvr.mem_maps.credo',
6365
'sonic_platform_base.sonic_xcvr.codes.credo',

sonic_platform_base/sonic_xcvr/api/arista/__init__.py

Whitespace-only changes.
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
"""
2+
cmis_enhanced_lpo.py
3+
4+
API for Arista Enhanced LPO modules.
5+
"""
6+
7+
from ..public.cmis import CmisApi
8+
from ...fields import arista_lpo_consts as lpo
9+
10+
# LPO_CAPABILITY is C1h:128. Bits 2, 3, and 4 advertise OER, VMA, and OMA support.
11+
_OER_CAPABILITY_MASK = 0x04
12+
_VMA_CAPABILITY_MASK = 0x08
13+
_OMA_CAPABILITY_MASK = 0x10
14+
15+
_VMA_FLAG_FIELDS = [
16+
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_FLAG,
17+
lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_FLAG,
18+
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_FLAG,
19+
lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_FLAG,
20+
]
21+
22+
_OMA_FLAG_FIELDS = [
23+
lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_FLAG,
24+
lpo.LPO_RX_INPUT_OMA_LOW_ALARM_FLAG,
25+
lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_FLAG,
26+
lpo.LPO_RX_INPUT_OMA_LOW_WARNING_FLAG,
27+
]
28+
29+
_VMA_TX_FIELDS = [
30+
lpo.LPO_HOST_INPUT_VMA_TX1,
31+
lpo.LPO_HOST_INPUT_VMA_TX2,
32+
lpo.LPO_HOST_INPUT_VMA_TX3,
33+
lpo.LPO_HOST_INPUT_VMA_TX4,
34+
lpo.LPO_HOST_INPUT_VMA_TX5,
35+
lpo.LPO_HOST_INPUT_VMA_TX6,
36+
lpo.LPO_HOST_INPUT_VMA_TX7,
37+
lpo.LPO_HOST_INPUT_VMA_TX8,
38+
]
39+
40+
_OMA_RX_FIELDS = [
41+
lpo.LPO_INPUT_OMA_RX1,
42+
lpo.LPO_INPUT_OMA_RX2,
43+
lpo.LPO_INPUT_OMA_RX3,
44+
lpo.LPO_INPUT_OMA_RX4,
45+
lpo.LPO_INPUT_OMA_RX5,
46+
lpo.LPO_INPUT_OMA_RX6,
47+
lpo.LPO_INPUT_OMA_RX7,
48+
lpo.LPO_INPUT_OMA_RX8,
49+
]
50+
51+
52+
_VMA_THRESHOLD_FIELDS = [
53+
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_THRESHOLD,
54+
lpo.LPO_TX_HOST_INPUT_VMA_LOW_ALARM_THRESHOLD,
55+
lpo.LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_THRESHOLD,
56+
lpo.LPO_TX_HOST_INPUT_VMA_LOW_WARNING_THRESHOLD,
57+
]
58+
59+
_OMA_THRESHOLD_FIELDS = [
60+
lpo.LPO_RX_INPUT_OMA_HIGH_ALARM_THRESHOLD,
61+
lpo.LPO_RX_INPUT_OMA_LOW_ALARM_THRESHOLD,
62+
lpo.LPO_RX_INPUT_OMA_HIGH_WARNING_THRESHOLD,
63+
lpo.LPO_RX_INPUT_OMA_LOW_WARNING_THRESHOLD,
64+
]
65+
66+
67+
def _value_or_na(value):
68+
"""Return the API fallback for decoded fields that failed to read."""
69+
return value if value is not None else 'N/A'
70+
71+
72+
def _read_value_or_na(xcvr_eeprom, field):
73+
"""Read an already-decoded EEPROM field and normalize read failures."""
74+
return _value_or_na(xcvr_eeprom.read(field))
75+
76+
77+
def _set_na(output, fields):
78+
"""Populate unsupported scalar fields without reading optional registers."""
79+
for field in fields:
80+
output[field] = 'N/A'
81+
82+
83+
def _set_lane_flag_na(output, fields):
84+
"""Populate unsupported per-lane flag fields with the API fallback value."""
85+
for field in fields:
86+
for lane in range(1, 9):
87+
output["{}{}".format(field, lane)] = 'N/A'
88+
89+
90+
def _lane_flag_na_dict(field):
91+
"""Build the per-lane fallback shape expected by decoded flag fields."""
92+
flags = {}
93+
_set_lane_flag_na(flags, [field])
94+
return flags
95+
96+
97+
def _is_capability_supported(capabilities, mask):
98+
"""Check whether an optional LPO capability bit is advertised."""
99+
return bool(capabilities & mask)
100+
101+
102+
class CmisEnhancedLpoApi(CmisApi):
103+
def _read_if_supported(self, field, capabilities, mask):
104+
"""Read optional info fields only when the capability bit is set."""
105+
if not _is_capability_supported(capabilities, mask):
106+
return 'N/A'
107+
return _read_value_or_na(self.xcvr_eeprom, field)
108+
109+
def get_transceiver_info(self):
110+
xcvr_info = super().get_transceiver_info()
111+
if xcvr_info is None:
112+
return None
113+
capabilities = self.xcvr_eeprom.read(lpo.LPO_CAPABILITY)
114+
if capabilities is None:
115+
return None
116+
117+
xcvr_info.update({
118+
lpo.LPO_TX_POLARITY_INVERTED: _read_value_or_na(self.xcvr_eeprom, lpo.LPO_TX_POLARITY_INVERTED),
119+
lpo.LPO_RX_POLARITY_INVERTED: _read_value_or_na(self.xcvr_eeprom, lpo.LPO_RX_POLARITY_INVERTED),
120+
lpo.LPO_TX_HOST_INPUT_VMA_MON_ACCURACY: self._read_if_supported(
121+
lpo.LPO_TX_HOST_INPUT_VMA_MON_ACCURACY, capabilities, _VMA_CAPABILITY_MASK
122+
),
123+
lpo.LPO_RX_INPUT_OMA_MON_ACCURACY: self._read_if_supported(
124+
lpo.LPO_RX_INPUT_OMA_MON_ACCURACY, capabilities, _OMA_CAPABILITY_MASK
125+
),
126+
lpo.LPO_TX_OUTER_EXTINCTION_RATIO_MAX: self._read_if_supported(
127+
lpo.LPO_TX_OUTER_EXTINCTION_RATIO_MAX, capabilities, _OER_CAPABILITY_MASK
128+
),
129+
})
130+
return xcvr_info
131+
132+
def get_transceiver_dom_real_value(self):
133+
trans_dom = super().get_transceiver_dom_real_value()
134+
if trans_dom is None:
135+
return None
136+
capabilities = self.xcvr_eeprom.read(lpo.LPO_CAPABILITY)
137+
if capabilities is None:
138+
return None
139+
140+
if _is_capability_supported(capabilities, _VMA_CAPABILITY_MASK):
141+
for field in _VMA_TX_FIELDS:
142+
trans_dom[field] = _read_value_or_na(self.xcvr_eeprom, field)
143+
else:
144+
_set_na(trans_dom, _VMA_TX_FIELDS)
145+
146+
if _is_capability_supported(capabilities, _OMA_CAPABILITY_MASK):
147+
for field in _OMA_RX_FIELDS:
148+
trans_dom[field] = _read_value_or_na(self.xcvr_eeprom, field)
149+
else:
150+
_set_na(trans_dom, _OMA_RX_FIELDS)
151+
152+
return trans_dom
153+
154+
def get_transceiver_threshold_info(self):
155+
threshold_dict = super().get_transceiver_threshold_info()
156+
if threshold_dict is None:
157+
return None
158+
capabilities = self.xcvr_eeprom.read(lpo.LPO_CAPABILITY)
159+
if capabilities is None:
160+
return None
161+
162+
if _is_capability_supported(capabilities, _VMA_CAPABILITY_MASK):
163+
for field in _VMA_THRESHOLD_FIELDS:
164+
threshold_dict[field] = _read_value_or_na(self.xcvr_eeprom, field)
165+
else:
166+
_set_na(threshold_dict, _VMA_THRESHOLD_FIELDS)
167+
168+
if _is_capability_supported(capabilities, _OMA_CAPABILITY_MASK):
169+
for field in _OMA_THRESHOLD_FIELDS:
170+
threshold_dict[field] = _read_value_or_na(self.xcvr_eeprom, field)
171+
else:
172+
_set_na(threshold_dict, _OMA_THRESHOLD_FIELDS)
173+
174+
return threshold_dict
175+
176+
def get_transceiver_dom_flags(self):
177+
dom_flag_dict = super().get_transceiver_dom_flags()
178+
if dom_flag_dict is None:
179+
return None
180+
capabilities = self.xcvr_eeprom.read(lpo.LPO_CAPABILITY)
181+
if capabilities is None:
182+
return None
183+
184+
if _is_capability_supported(capabilities, _VMA_CAPABILITY_MASK):
185+
for field in _VMA_FLAG_FIELDS:
186+
flags = self.xcvr_eeprom.read(field)
187+
dom_flag_dict.update(flags if flags is not None else _lane_flag_na_dict(field))
188+
else:
189+
_set_lane_flag_na(dom_flag_dict, _VMA_FLAG_FIELDS)
190+
191+
if _is_capability_supported(capabilities, _OMA_CAPABILITY_MASK):
192+
for field in _OMA_FLAG_FIELDS:
193+
flags = self.xcvr_eeprom.read(field)
194+
dom_flag_dict.update(flags if flags is not None else _lane_flag_na_dict(field))
195+
else:
196+
_set_lane_flag_na(dom_flag_dict, _OMA_FLAG_FIELDS)
197+
198+
return dom_flag_dict
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Enhanced LPO (Arista) - field groups
2+
LPO_EEPROM_FIELD = "LpoEepromField"
3+
LPO_INFO_FIELD = "LpoInfoField"
4+
LPO_VMA_THRESHOLDS_FIELD = "LpoVmaThresholdsField"
5+
LPO_OMA_THRESHOLDS_FIELD = "LpoOmaThresholdsField"
6+
LPO_VMA_FLAGS_FIELD = "LpoVmaFlagsField"
7+
LPO_VMA_DOM_FIELD = "LpoVmaDomField"
8+
LPO_VMA_MASKS_FIELD = "LpoVmaMasksField"
9+
LPO_OMA_FLAGS_FIELD = "LpoOmaFlagsField"
10+
LPO_OMA_DOM_FIELD = "LpoOmaDomField"
11+
LPO_OMA_MASKS_FIELD = "LpoOmaMasksField"
12+
13+
# Enhanced LPO - page 01h advertisement
14+
LPO_EEPROM_COMPLIANCE = "LPOEepromCompliance"
15+
LPO_ENHANCED_SPEC_VERSION = "LPOEnhancedSpecVersion"
16+
17+
# Enhanced LPO - page C1h info / capability
18+
LPO_CAPABILITY = "LPOCapability"
19+
LPO_TX_OUTER_EXTINCTION_RATIO_MAX = "LPOTxOuterExtinctionRatioMax"
20+
LPO_TX_POLARITY_INVERTED = "LPOTxPolarityInverted"
21+
LPO_RX_POLARITY_INVERTED = "LPORxPolarityInverted"
22+
LPO_TX_HOST_INPUT_VMA_MON_ACCURACY = "LPOTxHostInputVMAMonAccuracySupported"
23+
LPO_RX_INPUT_OMA_MON_ACCURACY = "LPORxInputOMAMonAccuracySupported"
24+
25+
# Enhanced LPO - page C1h VMA thresholds
26+
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_THRESHOLD = "LPOTxHostInputVMAHighAlarmThreshold"
27+
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_THRESHOLD = "LPOTxHostInputVMALowAlarmThreshold"
28+
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_THRESHOLD = "LPOTxHostInputVMAHighWarningThreshold"
29+
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_THRESHOLD = "LPOTxHostInputVMALowWarningThreshold"
30+
31+
# Enhanced LPO - page C1h OMA thresholds
32+
LPO_RX_INPUT_OMA_HIGH_ALARM_THRESHOLD = "LPORxInputOMAHighAlarmThreshold"
33+
LPO_RX_INPUT_OMA_LOW_ALARM_THRESHOLD = "LPORxInputOMALowAlarmThreshold"
34+
LPO_RX_INPUT_OMA_HIGH_WARNING_THRESHOLD = "LPORxInputOMAHighWarningThreshold"
35+
LPO_RX_INPUT_OMA_LOW_WARNING_THRESHOLD = "LPORxInputOMALowWarningThreshold"
36+
37+
# Enhanced LPO - page C2h VMA flags
38+
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_FLAG = "LPOTxHostInputVMAHighAlarmFlag"
39+
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_FLAG = "LPOTxHostInputVMALowAlarmFlag"
40+
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_FLAG = "LPOTxHostInputVMAHighWarningFlag"
41+
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_FLAG = "LPOTxHostInputVMALowWarningFlag"
42+
43+
# Enhanced LPO - page C2h per-lane VMA measurements
44+
LPO_HOST_INPUT_VMA_TX1 = "LPOHostInputVMATx1"
45+
LPO_HOST_INPUT_VMA_TX2 = "LPOHostInputVMATx2"
46+
LPO_HOST_INPUT_VMA_TX3 = "LPOHostInputVMATx3"
47+
LPO_HOST_INPUT_VMA_TX4 = "LPOHostInputVMATx4"
48+
LPO_HOST_INPUT_VMA_TX5 = "LPOHostInputVMATx5"
49+
LPO_HOST_INPUT_VMA_TX6 = "LPOHostInputVMATx6"
50+
LPO_HOST_INPUT_VMA_TX7 = "LPOHostInputVMATx7"
51+
LPO_HOST_INPUT_VMA_TX8 = "LPOHostInputVMATx8"
52+
53+
# Enhanced LPO - page C2h VMA masks
54+
LPO_TX_HOST_INPUT_VMA_HIGH_ALARM_MASK = "LPOTxHostInputVMAHighAlarmMask"
55+
LPO_TX_HOST_INPUT_VMA_LOW_ALARM_MASK = "LPOTxHostInputVMALowAlarmMask"
56+
LPO_TX_HOST_INPUT_VMA_HIGH_WARNING_MASK = "LPOTxHostInputVMAHighWarningMask"
57+
LPO_TX_HOST_INPUT_VMA_LOW_WARNING_MASK = "LPOTxHostInputVMALowWarningMask"
58+
59+
# Enhanced LPO - page C2h OMA flags
60+
LPO_RX_INPUT_OMA_HIGH_ALARM_FLAG = "LPORxInputOMAHighAlarmFlag"
61+
LPO_RX_INPUT_OMA_LOW_ALARM_FLAG = "LPORxInputOMALowAlarmFlag"
62+
LPO_RX_INPUT_OMA_HIGH_WARNING_FLAG = "LPORxInputOMAHighWarningFlag"
63+
LPO_RX_INPUT_OMA_LOW_WARNING_FLAG = "LPORxInputOMALowWarningFlag"
64+
65+
# Enhanced LPO - page C2h per-lane OMA measurements
66+
LPO_INPUT_OMA_RX1 = "LPOInputOMARx1"
67+
LPO_INPUT_OMA_RX2 = "LPOInputOMARx2"
68+
LPO_INPUT_OMA_RX3 = "LPOInputOMARx3"
69+
LPO_INPUT_OMA_RX4 = "LPOInputOMARx4"
70+
LPO_INPUT_OMA_RX5 = "LPOInputOMARx5"
71+
LPO_INPUT_OMA_RX6 = "LPOInputOMARx6"
72+
LPO_INPUT_OMA_RX7 = "LPOInputOMARx7"
73+
LPO_INPUT_OMA_RX8 = "LPOInputOMARx8"
74+
75+
# Enhanced LPO - page C2h OMA masks
76+
LPO_RX_INPUT_OMA_HIGH_ALARM_MASK = "LPORxInputOMAHighAlarmMask"
77+
LPO_RX_INPUT_OMA_LOW_ALARM_MASK = "LPORxInputOMALowAlarmMask"
78+
LPO_RX_INPUT_OMA_HIGH_WARNING_MASK = "LPORxInputOMAHighWarningMask"
79+
LPO_RX_INPUT_OMA_LOW_WARNING_MASK = "LPORxInputOMALowWarningMask"
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import struct
2+
3+
from .xcvr_field import NumberRegField
4+
5+
6+
class _LpoScaledNumberRegField(NumberRegField):
7+
factor = 1
8+
9+
def __init__(self, name, offset, *fields, **kwargs):
10+
super(_LpoScaledNumberRegField, self).__init__(name, offset, *fields, **kwargs)
11+
self.precision = kwargs.get("precision", 3)
12+
13+
def decode(self, raw_data, **decoded_deps):
14+
value = struct.unpack(self.format, raw_data)[0] * self.factor
15+
return float("{:.{}f}".format(value, self.precision))
16+
17+
18+
class _LpoLowerNibbleScaledRegField(_LpoScaledNumberRegField):
19+
def decode(self, raw_data, **decoded_deps):
20+
value = (struct.unpack(self.format, raw_data)[0] & 0x0F) * self.factor
21+
return float("{:.{}f}".format(value, self.precision))
22+
23+
24+
class LpoVmaField(_LpoScaledNumberRegField):
25+
factor = 5
26+
27+
28+
class LpoOmaField(_LpoScaledNumberRegField):
29+
factor = 0.0001
30+
31+
32+
class LpoOerField(_LpoScaledNumberRegField):
33+
factor = 0.1
34+
35+
36+
class LpoVmaAccuracyField(_LpoLowerNibbleScaledRegField):
37+
factor = 5
38+
39+
40+
class LpoOmaAccuracyField(_LpoLowerNibbleScaledRegField):
41+
factor = 0.2
42+
43+
44+
class LpoLaneFlagRegField(NumberRegField):
45+
def decode(self, raw_data, **decoded_deps):
46+
value = struct.unpack(self.format, raw_data)[0]
47+
decoded = {}
48+
for lane in range(1, 9):
49+
decoded["{}{}".format(self.name, lane)] = bool((value >> (lane - 1)) & 1)
50+
return decoded

sonic_platform_base/sonic_xcvr/mem_maps/arista/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)