File size: 8,024 Bytes
8c7dd56 a1e6013 2e4ea50 8c7dd56 bb78364 8c7dd56 2654ab0 2e4ea50 3266076 8c7dd56 ee5fb69 ccd6515 47f79ef a1e6013 47f79ef 2315207 ec345ab 51debf3 a1e6013 0ed3642 a1e6013 51debf3 a1e6013 51debf3 358e630 a1e6013 ee5fb69 a1e6013 51debf3 8c7dd56 ec345ab 7ffa76d ec345ab 7ffa76d ec345ab 7ffa76d ec345ab 7ffa76d ec345ab 7ffa76d ec345ab 7ffa76d 2315207 ec345ab 2315207 7ffa76d ec345ab a1e6013 8c7dd56 a1e6013 8c7dd56 6104d33 8a637a8 a1e6013 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | import os
from typing import Dict, Any, Optional
from dotenv import load_dotenv
# Project Paths
WORKSPACE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Load .env file ONLY if it exists, and NEVER override environment variables
# already set by the platform (e.g. HuggingFace Spaces secrets).
_dotenv_path = os.path.join(WORKSPACE_ROOT, ".env")
if os.path.isfile(_dotenv_path):
load_dotenv(_dotenv_path, override=False)
# For end-users, we want designs to generate exactly where they run the command.
# Developers can still override OPENLANE_ROOT with an environment variable.
OPENLANE_ROOT = os.environ.get("OPENLANE_ROOT", os.getcwd())
DESIGNS_DIR = os.path.join(OPENLANE_ROOT, "designs")
SCRIPTS_DIR = os.path.join(WORKSPACE_ROOT, "scripts")
CLOUD_CONFIG = {
"model": os.environ.get("NVIDIA_MODEL", "openai/meta/llama-3.3-70b-instruct"),
"base_url": os.environ.get("NVIDIA_BASE_URL", "https://integrate.api.nvidia.com/v1"),
"api_key": os.environ.get("NVIDIA_API_KEY", ""),
}
DEEPSEEK_CONFIG = {
"model": os.environ.get("DEEPSEEK_MODEL", "openai/deepseek-ai/deepseek-v3.2"),
"base_url": os.environ.get("DEEPSEEK_BASE_URL", "https://integrate.api.nvidia.com/v1"),
"api_key": os.environ.get("DEEPSEEK_API_KEY", ""),
"extra_body": {"chat_template_kwargs": {"thinking": True}}
}
GLM_CONFIG = {
"model": os.environ.get("GLM_MODEL", "glm-4-plus"),
"base_url": os.environ.get("GLM_BASE_URL", "https://open.bigmodel.cn/api/paas/v4/"),
"api_key": os.environ.get("GLM_API_KEY", ""),
}
LOCAL_CONFIG = {
"model": os.environ.get(
"LLM_MODEL",
"ollama/qwen2.5-coder:7b",
),
"base_url": os.environ.get("LLM_BASE_URL", "http://localhost:11434"),
"api_key": os.environ.get("LLM_API_KEY", "NA"),
}
GROQ_CONFIG = {
"model": os.environ.get("GROQ_MODEL", "groq/llama-3.3-70b-versatile"),
"base_url": "", # litellm resolves groq routing from the model prefix
"api_key": os.environ.get("GROQ_API_KEY", ""),
}
# Backward-compat alias used by parts of the codebase/docs
NVIDIA_CONFIG = CLOUD_CONFIG
# Expose active defaults (CLI chooses concrete backend)
LLM_MODEL = LOCAL_CONFIG["model"]
LLM_BASE_URL = LOCAL_CONFIG["base_url"]
LLM_API_KEY = LOCAL_CONFIG["api_key"]
def get_role_llm_config(role: str) -> Dict[str, str]:
"""
Resolve the LLM config for a specific multi-agent role.
Intelligently maps agent roles to preferred backend engines:
- Defaulting Heavy Logic (Architect, Designer, Fixer, etc.) to GLM-4-Plus
- Physical -> Groq (Blazing fast iterative speed)
- Documenter/Reporter -> NVIDIA (Excellent prose generation, lower precision required)
- Reasoning parts (Architect, Fixer, Debugger) -> DeepSeek via NVIDIA
"""
role = role.lower()
# 1. Select the preferred engine
preferred_engine = GLM_CONFIG
if role in ("fixer", "debugger", "reasoner"):
preferred_engine = DEEPSEEK_CONFIG
elif role in ("architect", "designer", "testbench_designer", "verifier", "manager", "physical"):
preferred_engine = GLM_CONFIG
elif role in ("documenter", "reporter", "doc_gen"):
preferred_engine = GROQ_CONFIG
# Helper to check if an engine has a valid API key
def is_valid(cfg):
k = cfg.get("api_key", "")
return bool(k and k not in ("mock-key", "NA", ""))
# 2. Fallback execution order (Preferred -> DeepSeek -> GLM -> NVIDIA -> Groq -> Local)
# If the user's setup lacks a key for the preferred engine, shift to the next best
engines = [preferred_engine, DEEPSEEK_CONFIG, GLM_CONFIG, CLOUD_CONFIG, GROQ_CONFIG, LOCAL_CONFIG]
for cfg in engines:
# LOCAL_CONFIG is always considered a valid fallback if we reach the end
if cfg is LOCAL_CONFIG or is_valid(cfg):
model = cfg.get("model", "")
# Ensure proper prefixing: use 'openai/' for custom OpenAI-compatible endpoints like Zhipu
if cfg is GLM_CONFIG and not model.startswith("openai/"):
model = f"openai/{model}"
if cfg is DEEPSEEK_CONFIG and not model.startswith("openai/"):
model = f"openai/{model}"
result = {
"model": model,
"api_key": cfg.get("api_key", ""),
"base_url": cfg.get("base_url", "")
}
if "extra_body" in cfg:
result["extra_body"] = cfg["extra_body"]
return result
return LOCAL_CONFIG.copy()
# Portable OSS-PDK profiles (adapter-style)
PDK_PROFILES: Dict[str, Dict[str, Any]] = {
"sky130": {
"pdk": "sky130A",
"std_cell_library": "sky130_fd_sc_hd",
"default_clock_period": "10.0",
},
"gf180": {
"pdk": "gf180mcuC",
"std_cell_library": "gf180mcu_fd_sc_mcu7t5v0",
"default_clock_period": "15.0",
},
}
DEFAULT_PDK_PROFILE = os.environ.get("PDK_PROFILE", "sky130").strip().lower()
if DEFAULT_PDK_PROFILE not in PDK_PROFILES:
DEFAULT_PDK_PROFILE = "sky130"
# Tool Settings
PDK_ROOT = os.environ.get("PDK_ROOT", os.path.expanduser("~/.ciel"))
PDK = os.environ.get("PDK", PDK_PROFILES[DEFAULT_PDK_PROFILE]["pdk"])
OPENLANE_IMAGE = "ghcr.io/the-openroad-project/openlane:ff5509f65b17bfa4068d5336495ab1718987ff69-amd64"
# Simulation/Coverage adapter defaults
SIM_BACKEND_DEFAULT = os.environ.get("SIM_BACKEND_DEFAULT", "auto").strip().lower()
if SIM_BACKEND_DEFAULT not in {"auto", "verilator", "iverilog"}:
SIM_BACKEND_DEFAULT = "auto"
COVERAGE_FALLBACK_POLICY_DEFAULT = os.environ.get("COVERAGE_FALLBACK_POLICY", "fallback_oss").strip().lower()
if COVERAGE_FALLBACK_POLICY_DEFAULT not in {"fail_closed", "fallback_oss", "skip"}:
COVERAGE_FALLBACK_POLICY_DEFAULT = "fallback_oss"
COVERAGE_PROFILE_DEFAULT = os.environ.get("COVERAGE_PROFILE", "balanced").strip().lower()
if COVERAGE_PROFILE_DEFAULT not in {"balanced", "aggressive", "relaxed"}:
COVERAGE_PROFILE_DEFAULT = "balanced"
def _resolve_tool_binary(bin_name: str, env_var: Optional[str] = None) -> str:
"""Resolve tool binary using configured roots before PATH.
Fallback order:
1) Explicit env var for that tool (if provided)
2) OSS_CAD_SUITE_HOME/bin
3) WORKSPACE_ROOT/oss-cad-suite/bin
4) /home/vickynishad/oss-cad-suite/bin
5) bin_name from PATH
"""
explicit = os.environ.get(env_var, "").strip() if env_var else ""
if explicit and os.path.exists(explicit):
return explicit
roots = []
oss_home = os.environ.get("OSS_CAD_SUITE_HOME", "").strip()
if oss_home:
roots.append(oss_home)
roots.append(os.path.join(WORKSPACE_ROOT, "oss-cad-suite"))
for root in roots:
candidate = os.path.join(root, "bin", bin_name)
if os.path.exists(candidate):
return candidate
return bin_name
OSS_CAD_SUITE_ROOT = os.environ.get("OSS_CAD_SUITE_HOME", os.path.join(WORKSPACE_ROOT, "oss-cad-suite"))
SBY_BIN = _resolve_tool_binary("sby", env_var="SBY_BIN")
YOSYS_BIN = _resolve_tool_binary("yosys", env_var="YOSYS_BIN")
EQY_BIN = _resolve_tool_binary("eqy", env_var="EQY_BIN")
def get_pdk_profile(profile: str) -> Dict[str, Any]:
key = (profile or DEFAULT_PDK_PROFILE).strip().lower()
if key not in PDK_PROFILES:
key = "sky130"
data = dict(PDK_PROFILES[key])
data["profile"] = key
return data
def get_toolchain_diagnostics() -> Dict[str, Any]:
"""Return resolved toolchain paths and existence info for startup checks."""
bins = {
"sby": SBY_BIN,
"yosys": YOSYS_BIN,
"eqy": EQY_BIN,
}
return {
"workspace_root": WORKSPACE_ROOT,
"openlane_root": OPENLANE_ROOT,
"pdk_root": PDK_ROOT,
"pdk": PDK,
"oss_cad_suite_home": os.environ.get("OSS_CAD_SUITE_HOME", ""),
"bins": {
name: {"path": path, "exists": os.path.exists(path) if os.path.isabs(path) else False}
for name, path in bins.items()
},
}
|