Skip to content

Commit

Permalink
[sai-gen] Simplify type solving, move enum type solving to use Sai at…
Browse files Browse the repository at this point in the history
…tribute, add default value in Sai attribute (#475)

With these changes, we will be able to specify the enum types and default value for any table key and action parameter as below:

```
@Sai[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN"]
dash_encapsulation_t dash_encapsulation,
```

This removes the dependency on the variable name which has to match with the type.

This makes the generated SAI matches what is in opencompute SAI.
  • Loading branch information
r12f authored Dec 12, 2023
1 parent b8750ef commit 256b533
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 78 deletions.
207 changes: 131 additions & 76 deletions dash-pipeline/SAI/sai_api_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,89 +81,139 @@ def parse(self, p4rt_value, *args, **kwargs):
# |- SAIAPITableAction <-------| : Information of a single P4 table action defined used by the table.
# |- SAIAPITableActionParam -| : Information of a single P4 table action parameter used by the action.
#
class SAITypeInfo:
def __init__(self, name, field_func_prefix, default = None, is_enum = False):
self.name = name
self.field_func_prefix = field_func_prefix
self.default = default
self.is_enum = is_enum

class SAITypeSolver:
sai_type_to_field = {
'bool': 'booldata',
'sai_uint8_t': 'u8',
'sai_object_id_t': 'u16',
'sai_uint16_t': 'u16',
'sai_ip_address_t': 'ipaddr',
'sai_ip_addr_family_t': 'u32',
'sai_uint32_t': 'u32',
'sai_uint64_t': 'u64',
'sai_mac_t': 'mac',
'sai_ip_prefix_list_t': 'ipprefixlist'
sai_type_info_registry = {
"bool": SAITypeInfo("bool", "booldata"),
"sai_uint8_t": SAITypeInfo("sai_uint8_t", "u8"),
"sai_object_id_t": SAITypeInfo("sai_object_id_t", "u16"),
"sai_uint16_t": SAITypeInfo("sai_uint16_t", "u16"),
"sai_ip_address_t": SAITypeInfo("sai_ip_address_t", "ipaddr"),
"sai_ip_addr_family_t": SAITypeInfo("sai_ip_addr_family_t", "u32"),
"sai_uint32_t": SAITypeInfo("sai_uint32_t", "u32"),
"sai_uint64_t": SAITypeInfo("sai_uint64_t", "u64"),
"sai_mac_t": SAITypeInfo("sai_mac_t", "mac"),
"sai_ip_prefix_t": SAITypeInfo("sai_ip_prefix_t", "ipPrefix"),
"sai_u8_list_t": SAITypeInfo("sai_u8_list_t", "u8list"),
"sai_u16_list_t": SAITypeInfo("sai_u16_list_t", "u16list"),
"sai_u32_list_t": SAITypeInfo("sai_u32_list_t", "u32list"),
"sai_ip_prefix_list_t": SAITypeInfo("sai_ip_prefix_list_t", "ipprefixlist"),
"sai_u8_range_list_t": SAITypeInfo("sai_u8_range_list_t", "u8rangelist"),
"sai_u16_range_list_t": SAITypeInfo("sai_u16_range_list_t", "u16rangelist"),
"sai_u32_range_list_t": SAITypeInfo("sai_u32_range_list_t", "u32rangelist"),
"sai_u64_range_list_t": SAITypeInfo("sai_u64_range_list_t", "u64rangelist"),
"sai_ipaddr_range_list_t": SAITypeInfo("sai_ipaddr_range_list_t", "ipaddrrangelist"),
}

@staticmethod
def get_sai_default_field_from_type(sai_type):
return SAITypeSolver.sai_type_to_field[sai_type]
def register_sai_type(name, field_func_prefix, default = None, is_enum = False):
SAITypeSolver.sai_type_info_registry[name] = SAITypeInfo(name, field_func_prefix=field_func_prefix, default=default, is_enum=is_enum)

@staticmethod
def get_sai_key_type(key_size, key_header, key_field):
if key_size == 1:
return 'bool', "booldata"
elif key_size <= 8:
return 'sai_uint8_t', "u8"
elif key_size == 16 and ('_id' in key_field):
return 'sai_object_id_t', "u16"
elif key_size <= 16:
return 'sai_uint16_t', "u16"
elif key_size == 32 and ('ip_addr_family' in key_field):
return 'sai_ip_addr_family_t', "u32"
elif key_size == 32 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ip_address_t', "ipaddr"
elif key_size == 32 and ('_id' in key_field):
return 'sai_object_id_t', "u32"
elif key_size <= 32:
return 'sai_uint32_t', "u32"
elif key_size == 48 and ('addr' in key_field or 'mac' in key_header):
return 'sai_mac_t', "mac"
elif key_size <= 64:
return 'sai_uint64_t', "u64"
elif key_size == 128:
return 'sai_ip_address_t', "ipaddr"
def get_sai_type(sai_type):
return SAITypeSolver.sai_type_info_registry[sai_type]

@staticmethod
def get_object_sai_type(object_size, object_parent_name, object_name):
sai_type_name = ""

if object_size == 1:
sai_type_name = 'bool'
elif object_size <= 8:
sai_type_name = 'sai_uint8_t'
elif object_size == 16 and ('_id' in object_name):
sai_type_name = 'sai_object_id_t'
elif object_size <= 16:
sai_type_name = 'sai_uint16_t'
elif object_size == 32 and ('ip_addr_family' in object_name):
sai_type_name = 'sai_ip_addr_family_t'
elif object_size == 32 and ('addr' in object_name or 'ip' in object_parent_name):
sai_type_name = 'sai_ip_address_t'
elif object_size == 32 and ('_id' in object_name):
sai_type_name = 'sai_object_id_t'
elif object_size <= 32:
sai_type_name = 'sai_uint32_t'
elif object_size == 48 and ('addr' in object_name or 'mac' in object_parent_name):
sai_type_name = 'sai_mac_t'
elif object_size <= 64:
sai_type_name = 'sai_uint64_t'
elif object_size == 128:
sai_type_name = 'sai_ip_address_t'
else:
raise ValueError(f'key_size={key_size} is not supported')
raise ValueError(f'key_size={object_size} is not supported')

return SAITypeSolver.get_sai_type(sai_type_name)

@staticmethod
def get_sai_lpm_type(key_size, key_header, key_field):
if key_size == 32 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ip_prefix_t', 'ipPrefix'
elif key_size == 128 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ip_prefix_t', 'ipPrefix'
raise ValueError(f'key_size={key_size}, key_header={key_header}, and key_field={key_field} is not supported')
def get_match_key_sai_type(match_type, key_size, key_parent_name, key_name):
if match_type == 'exact' or match_type == 'optional' or match_type == 'ternary':
return SAITypeSolver.get_object_sai_type(key_size, key_parent_name, key_name)
elif match_type == 'lpm':
return SAITypeSolver.__get_lpm_match_key_sai_type(key_size, key_parent_name, key_name)
elif match_type == 'list':
return SAITypeSolver.__get_list_match_key_sai_type(key_size, key_parent_name, key_name)
elif match_type == 'range_list':
return SAITypeSolver.__get_range_list_sai_type(key_size, key_parent_name, key_name)
else:
raise ValueError(f"match_type={match_type} is not supported")

@staticmethod
def get_sai_list_type(key_size, key_header, key_field):
def __get_lpm_match_key_sai_type(key_size, key_parent_name, key_name):
sai_type_name = ""

if key_size == 32 and ('addr' in key_name or 'ip' in key_parent_name):
sai_type_name = 'sai_ip_prefix_t'
elif key_size == 128 and ('addr' in key_name or 'ip' in key_parent_name):
sai_type_name = 'sai_ip_prefix_t'
else:
raise ValueError(f'key_size={key_size}, key_header={key_parent_name}, and key_field={key_name} is not supported')

return SAITypeSolver.get_sai_type(sai_type_name)

@staticmethod
def __get_list_match_key_sai_type(key_size, key_header, key_field):
sai_type_name = ""

if key_size <= 8:
return 'sai_u8_list_t', "u8list"
sai_type_name = 'sai_u8_list_t'
elif key_size <= 16:
return 'sai_u16_list_t', "u16list"
sai_type_name = 'sai_u16_list_t'
elif key_size == 32 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ip_prefix_list_t', "ipprefixlist"
sai_type_name = 'sai_ip_prefix_list_t'
elif key_size <= 32:
return 'sai_u32_list_t', "u32list"
sai_type_name = 'sai_u32_list_t'
elif key_size == 128 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ip_prefix_list_t', "ipprefixlist"
sai_type_name = 'sai_ip_prefix_list_t'
else:
raise ValueError(f'key_size={key_size} is not supported')

return SAITypeSolver.get_sai_type(sai_type_name)

@staticmethod
def get_sai_range_list_type(key_size, key_header, key_field):
def __get_range_list_sai_type(key_size, key_header, key_field):
sai_type_name = ""

if key_size <= 8:
return 'sai_u8_range_list_t', 'u8rangelist'
sai_type_name = 'sai_u8_range_list_t'
elif key_size <= 16:
return 'sai_u16_range_list_t', 'u16rangelist'
sai_type_name = 'sai_u16_range_list_t'
elif key_size == 32 and ('addr' in key_field or 'ip' in key_header):
return 'sai_ipaddr_range_list_t', 'ipaddrrangelist'
sai_type_name = 'sai_ipaddr_range_list_t'
elif key_size <= 32:
return 'sai_u32_range_list_t', 'u32rangelist'
sai_type_name = 'sai_u32_range_list_t'
elif key_size <= 64:
return 'sai_u64_range_list_t', 'u64rangelist'
sai_type_name = 'sai_u64_range_list_t'
else:
raise ValueError(f'key_size={key_size} is not supported')

return SAITypeSolver.get_sai_type(sai_type_name)


class SAIObject:
def __init__(self):
Expand All @@ -179,6 +229,7 @@ def __init__(self):
self.object_name = None
self.skipattr = None
self.field = None
self.default = None

def parse_preamble_if_exists(self, p4rt_object):
'''
Expand Down Expand Up @@ -261,6 +312,8 @@ def _parse_sai_object_annotation(self, p4rt_anno_list):
for kv in anno[KV_PAIR_LIST_TAG][KV_PAIRS_TAG]:
if kv['key'] == 'type':
self.type = kv['value']['stringValue']
elif kv['key'] == 'default_value': # "default" is a reserved keyword and cannot be used.
self.default = kv['value']['stringValue']
elif kv['key'] == 'isresourcetype':
self.isresourcetype = kv['value']['stringValue']
elif kv['key'] == 'isreadonly':
Expand All @@ -272,7 +325,10 @@ def _parse_sai_object_annotation(self, p4rt_anno_list):
else:
raise ValueError("Unknown attr annotation " + kv['key'])

self.field = SAITypeSolver.get_sai_default_field_from_type(self.type)
sai_type_info = SAITypeSolver.get_sai_type(self.type)
self.field = sai_type_info.field_func_prefix
if self.default == None and sai_type_info.is_enum:
self.default = sai_type_info.default


@sai_parser_from_p4rt
Expand Down Expand Up @@ -302,6 +358,7 @@ class SAIEnum(SAIObject):
'''
def __init__(self):
super().__init__()
self.bitwidth = 0
self.members = []

def parse_p4rt(self, p4rt_enum):
Expand All @@ -320,9 +377,18 @@ def parse_p4rt(self, p4rt_enum):
}
'''
print("Parsing enum: " + self.name)

self.name = self.name[:-2]
self.bitwidth = p4rt_enum['underlyingType'][BITWIDTH_TAG]
self.members = [SAIEnumMember.from_p4rt(enum_member['value'], name = enum_member['name']) for enum_member in p4rt_enum[MEMBERS_TAG]]

# Register enum type info.
SAITypeSolver.register_sai_type(
'sai_' + self.name + '_t',
"s32",
default = 'SAI_' + self.name.upper() + '_INVALID',
is_enum = True)


@sai_parser_from_p4rt
class SAIAPITableKey(SAIObject):
Expand Down Expand Up @@ -375,16 +441,9 @@ def parse_p4rt(self, p4rt_table_key, ip_is_v6_key_ids):
if STRUCTURED_ANNOTATIONS_TAG in p4rt_table_key:
self._parse_sai_object_annotation(p4rt_table_key)
else:
if self.match_type == 'exact' or self.match_type == 'optional' or self.match_type == 'ternary':
self.type, self.field = SAITypeSolver.get_sai_key_type(self.bitwidth, key_header, key_field)
elif self.match_type == 'lpm':
self.type, self.field = SAITypeSolver.get_sai_lpm_type(self.bitwidth, key_header, key_field)
elif self.match_type == 'list':
self.type, self.field = SAITypeSolver.get_sai_list_type(self.bitwidth, key_header, key_field)
elif self.match_type == 'range_list':
self.type, self.field = SAITypeSolver.get_sai_range_list_type(self.bitwidth, key_header, key_field)
else:
raise ValueError(f"match_type={self.match_type} is not supported")
sai_type_info = SAITypeSolver.get_match_key_sai_type(self.match_type, self.bitwidth, key_header, key_field)
self.type = sai_type_info.name
self.field = sai_type_info.field_func_prefix

# If *_is_v6 key is present, save its id.
ip_is_v6_key_name = self.sai_key_name + "_is_v6"
Expand Down Expand Up @@ -436,7 +495,6 @@ def parse_action_params(self, p4rt_table_action, sai_enums):

# Parse all params.
for p in p4rt_table_action[PARAMS_TAG]:
param_name = p[NAME_TAG]
param = SAIAPITableActionParam.from_p4rt(p, sai_enums = sai_enums, ip_is_v6_param_ids = ip_is_v6_param_ids)
self.params.append(param)

Expand All @@ -448,7 +506,6 @@ class SAIAPITableActionParam(SAIObject):
def __init__(self):
super().__init__()
self.bitwidth = 0
self.default = None
self.ip_is_v6_field_id = 0
self.param_actions = []

Expand All @@ -462,19 +519,17 @@ def parse_p4rt(self, p4rt_table_action_param, sai_enums, ip_is_v6_param_ids):
'''
self.id = p4rt_table_action_param['id']
self.name = p4rt_table_action_param[NAME_TAG]
self.bitwidth = p4rt_table_action_param[BITWIDTH_TAG]
#print("Parsing table action param: " + self.name)

if STRUCTURED_ANNOTATIONS_TAG in p4rt_table_action_param:
self._parse_sai_object_annotation(p4rt_table_action_param)
else:
self.type, self.field = SAITypeSolver.get_sai_key_type(int(p4rt_table_action_param[BITWIDTH_TAG]), p4rt_table_action_param[NAME_TAG], p4rt_table_action_param[NAME_TAG])
for sai_enum in sai_enums:
if self.name == sai_enum.name:
self.type = 'sai_' + self.name + '_t'
self.field = 's32'
self.default = 'SAI_' + self.name.upper() + '_INVALID'

self.bitwidth = p4rt_table_action_param[BITWIDTH_TAG]
sai_type_info = SAITypeSolver.get_object_sai_type(self.bitwidth, self.name, self.name)
print("Parsing table action param: " + self.name + ", type: " + sai_type_info.name, ", is_enum: " + str(sai_type_info.is_enum))
self.type, self.field = sai_type_info.name, sai_type_info.field_func_prefix
if sai_type_info.is_enum:
self.default = sai_type_info.default

# If *_is_v6 key is present, save its id.
ip_is_v6_param_name = self.name + "_is_v6"
Expand Down
5 changes: 3 additions & 2 deletions dash-pipeline/bmv2/dash_outbound.p4
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ control outbound(inout headers_t hdr,
IPv4ORv6Address underlay_dip,
bit<1> underlay_sip_is_v6,
IPv4ORv6Address underlay_sip,
@Sai[type="sai_dash_encapsulation_t", default_value="SAI_DASH_ENCAPSULATION_VXLAN"]
dash_encapsulation_t dash_encapsulation,
bit<24> tunnel_key,
bit<1> meter_policy_en,
Expand Down Expand Up @@ -119,7 +120,7 @@ control outbound(inout headers_t hdr,
}

action set_tunnel(IPv4Address underlay_dip,
dash_encapsulation_t dash_encapsulation,
@Sai[type="sai_dash_encapsulation_t"] dash_encapsulation_t dash_encapsulation,
bit<16> meter_class,
bit<1> meter_class_override) {
meta.encap_data.underlay_dip = underlay_dip;
Expand All @@ -146,7 +147,7 @@ control outbound(inout headers_t hdr,
action set_private_link_mapping(IPv4Address underlay_dip,
IPv6Address overlay_sip,
IPv6Address overlay_dip,
dash_encapsulation_t dash_encapsulation,
@Sai[type="sai_dash_encapsulation_t"] dash_encapsulation_t dash_encapsulation,
bit<24> tunnel_key,
bit<16> meter_class,
bit<1> meter_class_override) {
Expand Down

0 comments on commit 256b533

Please sign in to comment.