Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions cmake/AsicConfigV3ConfigCli.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# CMake to build libraries and binaries in fboss/lib/asic_config_v3
# Data-driven ASIC config generation

include(FBPythonBinary)

set(
ASIC_CONFIG_V3_PY_SRCS
"fboss/lib/asic_config_v3/__init__.py"
"fboss/lib/asic_config_v3/base_generator.py"
"fboss/lib/asic_config_v3/gen.py"
"fboss/lib/asic_config_v3/generators/__init__.py"
"fboss/lib/asic_config_v3/generators/broadcom_xgs_generator.py"
"fboss/lib/platform_mapping_v2/asic_vendor_config.py"
"fboss/lib/platform_mapping_v2/gen.py"
"fboss/lib/platform_mapping_v2/helpers.py"
"fboss/lib/platform_mapping_v2/platform_mapping_v2.py"
"fboss/lib/platform_mapping_v2/port_profile_mapping.py"
"fboss/lib/platform_mapping_v2/profile_settings.py"
"fboss/lib/platform_mapping_v2/read_files_utils.py"
"fboss/lib/platform_mapping_v2/si_settings.py"
"fboss/lib/platform_mapping_v2/static_mapping.py"
)

add_fb_thrift_python_executable(
fboss-asic-config-v3-gen
MAIN_MODULE fboss.lib.asic_config_v3.gen:generate_all_asic_configs
SOURCES ${ASIC_CONFIG_V3_PY_SRCS}
DEPENDS
platform_config_python
switch_config_python
transceiver_python
phy_python
platform_mapping_config_python
fboss_common_python
python-pyyaml::python-pyyaml
)

install_fb_python_executable(fboss-asic-config-v3-gen)
5 changes: 4 additions & 1 deletion fboss/lib/asic_config_v2/run-helper.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
#!/bin/bash
python3 fboss/lib/oss/run-helper.py --target fboss-asic-config-gen "$@"
python3 fboss/lib/oss/run-helper.py \
--target fboss-asic-config-gen.GEN_PY_EXE \
--extra-cmake-defines='{"RANGE_V3_TESTS": "OFF"}' \
"$@"
1 change: 1 addition & 0 deletions fboss/lib/asic_config_v3/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# pyre-strict
82 changes: 82 additions & 0 deletions fboss/lib/asic_config_v3/base_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# pyre-strict

import copy
import os
from abc import ABC, abstractmethod
from typing import Any

# Resolve config paths relative to the repo root rather than this file so the
# generator can run from the same checkout layout the bundled getdeps build
# expects.
_FBOSS_DIR: str = os.getcwd() + "/fboss"
MODULE_DIR: str = f"{_FBOSS_DIR}/lib/asic_config_v3"


def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
"""Recursively merge ``override`` on top of ``base``.

Dict values present in both are merged recursively. For any other value
type (scalars, lists, or a type mismatch between the two sides) the
override replaces the base entry outright. Returns a new dict; the inputs
are not mutated.
"""
result = copy.deepcopy(base)
for key, ov_value in override.items():
if (
key in result
and isinstance(result[key], dict)
and isinstance(ov_value, dict)
):
result[key] = _deep_merge(result[key], ov_value)
else:
result[key] = copy.deepcopy(ov_value)
return result


class BaseAsicConfigGenerator(ABC):
"""Abstract base class for ASIC config generators.

Subclasses implement ``generate()`` to produce vendor-specific output
such as YAML or JSON.
"""

def __init__(
self,
platform_name: str,
variant: str,
platform_config: dict[str, Any],
) -> None:
self.platform_name = platform_name
self.variant = variant
self.platform_config = platform_config

vendor = platform_config.get("vendor")
asic = platform_config.get("asic")
if not vendor:
raise ValueError("platform asic_config.json must define 'vendor'")
if not asic:
raise ValueError("platform asic_config.json must define 'asic'")
self.asic_vendor: str = vendor
self.asic_name: str = asic

# The platform JSON may declare a top-level ``defaults`` block inherited
# by every variant. The effective variant config is produced by deep-
# merging the variant-specific entries on top of ``defaults``. Dict
# values are merged recursively; scalars and lists are replaced.
defaults = platform_config.get("defaults", {})
variant_override = platform_config.get("variants", {}).get(variant, {})
self.variant_config: dict[str, Any] = _deep_merge(defaults, variant_override)
self.asic_config_params: dict[str, Any] = self.variant_config.get(
"asic_config_params", {}
)

@abstractmethod
def generate(self) -> str:
"""Generate the complete ASIC config and return it as a string."""
...

@property
@abstractmethod
def output_extension(self) -> str:
"""File extension for the generated output (e.g. '.yml' or '.json')."""
...
4 changes: 4 additions & 0 deletions fboss/lib/asic_config_v3/common/ocp_sai_common.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"_comment": "OCP SAI standard settings (vendor-agnostic).",
"global": {}
}
115 changes: 115 additions & 0 deletions fboss/lib/asic_config_v3/gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# pyre-strict

import json
import os
import sys

from fboss.lib.asic_config_v3.base_generator import (
BaseAsicConfigGenerator,
MODULE_DIR,
)
from fboss.lib.asic_config_v3.generators.broadcom_xgs_generator import (
BroadcomXgsGenerator,
)

OUTPUT_DIR: str = f"{MODULE_DIR}/generated_asic_configs"

# Add a new (vendor, asic) entry when bringing up a new ASIC family.
_GENERATOR_REGISTRY: dict[tuple[str, str], type[BaseAsicConfigGenerator]] = {
("broadcom", "tomahawk5"): BroadcomXgsGenerator,
("broadcom", "tomahawk6"): BroadcomXgsGenerator,
}


def get_generator(
platform_name: str, variant: str, platform_config: dict
) -> BaseAsicConfigGenerator:
"""Instantiate the correct generator based on vendor and ASIC."""
vendor = platform_config["vendor"]
asic = platform_config["asic"]
key = (vendor, asic)
generator_cls = _GENERATOR_REGISTRY.get(key)
if not generator_cls:
raise ValueError(f"No generator registered for vendor={vendor}, asic={asic}")
return generator_cls(platform_name, variant, platform_config)


def discover_platforms() -> dict:
"""Return a mapping of platform name to platform config.

Discovered by scanning ``platforms/*/asic_config.json``.
"""
platforms_dir = os.path.join(MODULE_DIR, "platforms")
platforms = {}

if not os.path.exists(platforms_dir):
return platforms

for platform_name in os.listdir(platforms_dir):
platform_path = os.path.join(platforms_dir, platform_name)
if not os.path.isdir(platform_path):
continue

config_path = os.path.join(platform_path, "asic_config.json")
if not os.path.exists(config_path):
continue

with open(config_path) as f:
platform_config = json.load(f)
platforms[platform_name] = platform_config

return platforms


def generate_all_asic_configs() -> None:
"""Generate ASIC configs for every discovered platform and variant."""
os.makedirs(OUTPUT_DIR, exist_ok=True)

for filename in os.listdir(OUTPUT_DIR):
if filename.endswith((".json", ".yml")):
os.remove(os.path.join(OUTPUT_DIR, filename))

platforms = discover_platforms()

for platform_name, platform_config in platforms.items():
vendor = platform_config.get("vendor", "")
asic = platform_config.get("asic", "")

if (vendor, asic) not in _GENERATOR_REGISTRY:
print(
f"Skipping {platform_name} (no generator for vendor={vendor}, asic={asic})",
file=sys.stderr,
)
continue

variants = platform_config.get("variants", {})

for variant_name in variants:
print(
f"Generating ASIC config for {platform_name}/{variant_name}...",
file=sys.stderr,
)

try:
generator = get_generator(platform_name, variant_name, platform_config)
output = generator.generate()

output_filename = (
f"{platform_name}_{variant_name}{generator.output_extension}"
)
output_path = os.path.join(OUTPUT_DIR, output_filename)

print(f"Writing to {output_path}", file=sys.stderr)
with open(output_path, "w", encoding="utf-8") as f:
f.write(output)

except Exception as e:
print(
f"Error generating config for {platform_name}/{variant_name}: {e}",
file=sys.stderr,
)
raise


if __name__ == "__main__":
generate_all_asic_configs()
1 change: 1 addition & 0 deletions fboss/lib/asic_config_v3/generators/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# pyre-strict
Loading
Loading