File size: 15,587 Bytes
b4f7b22 1d4d3e9 b4f7b22 1d4d3e9 b4f7b22 1d4d3e9 b4f7b22 1d4d3e9 b4f7b22 | 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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | """
AgentIC Stage Summary Generator
Generates human-readable stage completion summaries using the LLM.
"""
import json
import time
import logging
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
# Next stage mapping
STAGE_FLOW = [
"INIT", "SPEC", "SPEC_VALIDATE", "HIERARCHY_EXPAND", "FEASIBILITY_CHECK", "CDC_ANALYZE", "VERIFICATION_PLAN", "RTL_GEN", "RTL_FIX", "VERIFICATION",
"FORMAL_VERIFY", "COVERAGE_CHECK", "REGRESSION",
"SDC_GEN", "FLOORPLAN", "HARDENING", "CONVERGENCE_REVIEW",
"ECO_PATCH", "SIGNOFF", "SUCCESS",
]
STAGE_DESCRIPTIONS = {
"INIT": "Initialize workspace, check tool availability, and prepare build directories",
"SPEC": "Decompose natural language description into a structured architecture specification (SID JSON)",
"SPEC_VALIDATE": "Run 6-stage hardware spec validation: classify design, check completeness, decompose modules, define interfaces, generate behavioral contract",
"HIERARCHY_EXPAND": "Evaluate submodule complexity, recursively expand complex submodules into nested specs, and verify interface consistency across the full hierarchy",
"FEASIBILITY_CHECK": "Evaluate Sky130/OpenLane physical design feasibility: frequency limits, memory sizing, arithmetic complexity, area budget, and PDK-specific rules",
"CDC_ANALYZE": "Identify clock domain crossings, assign synchronization strategies (2-flop sync, pulse sync, async FIFO, handshake, reset sync), and generate CDC submodule specifications",
"RTL_GEN": "Generate Verilog/SystemVerilog RTL code from the architecture specification",
"RTL_FIX": "Run syntax checks and fix any Verilog syntax errors in the generated RTL",
"VERIFICATION": "Generate a testbench and run functional simulation to verify RTL correctness",
"FORMAL_VERIFY": "Write SystemVerilog Assertions and run formal property verification",
"COVERAGE_CHECK": "Run simulation with coverage instrumentation and analyze line/branch/toggle coverage",
"REGRESSION": "Run regression tests across multiple scenarios and corner cases",
"SDC_GEN": "Generate Synopsys Design Constraints (SDC) for timing/clock definitions",
"FLOORPLAN": "Generate floorplan configuration and run physical placement",
"HARDENING": "Run OpenLane GDSII hardening flow (synthesis β PnR β signoff)",
"CONVERGENCE_REVIEW": "Analyze timing/congestion/area convergence and decide on strategy pivots",
"ECO_PATCH": "Apply Engineering Change Orders to fix post-layout violations",
"SIGNOFF": "Run DRC, LVS, STA, and power signoff checks",
"SUCCESS": "Build completed successfully β all quality gates passed",
"FAIL": "Build failed β see error log for details",
}
def get_next_stage(current_stage: str) -> Optional[str]:
"""Get the next stage in the pipeline."""
try:
idx = STAGE_FLOW.index(current_stage)
if idx + 1 < len(STAGE_FLOW):
return STAGE_FLOW[idx + 1]
except ValueError:
pass
return None
def collect_stage_artifacts(orchestrator, stage_name: str) -> List[Dict[str, str]]:
"""Collect artifacts produced in a given stage."""
artifacts = []
art = orchestrator.artifacts or {}
artifact_map = {
"INIT": [
("root", "Build workspace root directory"),
("startup_check", "Startup diagnostics report"),
],
"SPEC": [
("sid", "Structured Interface Document (SID JSON)"),
("spec", "Detailed RTL generation prompt from SID"),
],
"SPEC_VALIDATE": [
("hardware_spec", "Validated hardware specification (JSON)"),
("spec_enrichment", "Behavioral contract and verification hints from spec validation"),
],
"HIERARCHY_EXPAND": [
("hierarchy_result", "Expanded hierarchy specification (JSON)"),
("hierarchy_enrichment", "Hierarchy depth, expansion count, and consistency fixes"),
],
"FEASIBILITY_CHECK": [
("feasibility_result", "Physical design feasibility analysis (JSON)"),
("feasibility_enrichment", "Feasibility verdict, GE estimate, floorplan recommendation, warnings"),
],
"CDC_ANALYZE": [
("cdc_result", "Clock domain crossing analysis (JSON)"),
("cdc_enrichment", "CDC status, domain count, crossing signals, synchronization submodules"),
],
"VERIFICATION_PLAN": [
("verification_plan", "Structured verification plan (JSON)"),
("verification_enrichment", "Test counts, SVA count, coverage points, warnings"),
],
"RTL_GEN": [
("rtl_path", "Generated Verilog RTL file"),
("rtl_code", "RTL source code content"),
],
"RTL_FIX": [
("rtl_path", "Syntax-fixed Verilog RTL file"),
],
"VERIFICATION": [
("tb_path", "Testbench file"),
("sim_result", "Simulation result output"),
("vcd_path", "Value Change Dump (VCD) waveform"),
],
"FORMAL_VERIFY": [
("formal_result", "Formal verification result"),
("sby_path", "SymbiYosys configuration file"),
],
"COVERAGE_CHECK": [
("coverage", "Coverage analysis results"),
],
"REGRESSION": [
("regression_result", "Regression test results"),
],
"SDC_GEN": [
("sdc_path", "SDC timing constraints file"),
],
"FLOORPLAN": [
("floorplan_tcl", "Floorplan TCL script"),
("openlane_config", "OpenLane configuration JSON"),
],
"HARDENING": [
("gds_path", "GDSII layout file"),
("def_path", "DEF placement file"),
],
"CONVERGENCE_REVIEW": [
("convergence_snapshot", "Timing/area/congestion convergence data"),
],
"ECO_PATCH": [
("eco_patch", "ECO patch applied"),
],
"SIGNOFF": [
("signoff_result", "DRC/LVS/STA signoff report"),
],
}
stage_artifacts = artifact_map.get(stage_name, [])
for key, desc in stage_artifacts:
value = art.get(key)
if value is not None:
path = value if isinstance(value, str) else json.dumps(value)[:200]
artifacts.append({
"name": key,
"path": path[:500],
"description": desc,
})
return artifacts
def collect_stage_decisions(orchestrator, stage_name: str) -> List[str]:
"""Collect decisions made during a stage from build history."""
decisions = []
# Check strategy pivots
if orchestrator.pivot_count > 0:
decisions.append(f"Strategy pivot #{orchestrator.pivot_count} applied (now using {orchestrator.strategy.value})")
# Check retry counts
retries = orchestrator.state_retry_counts.get(stage_name, 0)
if retries > 0:
decisions.append(f"Stage was retried {retries} time(s)")
# Check specific decisions from history
for entry in orchestrator.build_history:
if entry.state == stage_name:
msg = entry.message.lower()
if "fallback" in msg or "pivot" in msg or "strategy" in msg:
decisions.append(entry.message[:200])
elif "gate" in msg and ("pass" in msg or "fail" in msg):
decisions.append(entry.message[:200])
return decisions[:10] # Cap at 10
def collect_stage_warnings(orchestrator, stage_name: str) -> List[str]:
"""Collect warnings from a stage."""
warnings = []
for entry in orchestrator.build_history:
if entry.state == stage_name:
msg = entry.message.lower()
if any(w in msg for w in ["warn", "near-fail", "degraded", "threshold", "exceeded", "timeout"]):
warnings.append(entry.message[:200])
return warnings[:10]
def get_stage_log_summary(orchestrator, stage_name: str) -> str:
"""Get a condensed log of what happened in a stage."""
lines = []
for entry in orchestrator.build_history:
if entry.state == stage_name:
lines.append(entry.message)
return "\n".join(lines[-30:]) # Last 30 log lines
def generate_stage_summary_llm(llm, stage_name: str, design_name: str,
stage_log: str, artifacts: List[dict],
decisions: List[str], next_stage: Optional[str]) -> dict:
"""Call the LLM to generate a human-readable stage summary.
Returns: {"summary": str, "next_stage_preview": str}
"""
artifact_list = "\n".join(
f"- {a['name']}: {a['description']} (path: {a['path'][:100]})"
for a in artifacts
) or "No artifacts produced."
decisions_list = "\n".join(f"- {d}" for d in decisions) or "No autonomous decisions."
next_stage_desc = STAGE_DESCRIPTIONS.get(next_stage, "Unknown") if next_stage else "Build complete."
next_stage_label = next_stage or "N/A"
next_stage_desc_text = STAGE_DESCRIPTIONS.get(next_stage, "Unknown") if next_stage else "Build complete."
prompt = (
f"You just completed the {stage_name} stage of an autonomous chip design pipeline "
f"for the design '{design_name}'.\n\n"
f"Stage log (last events):\n{stage_log[:2000]}\n\n"
f"Respond in exactly 2 sentences. No more.\n"
f"Sentence 1: What just completed in the {stage_name} stage in plain simple language β "
f"one specific thing that was done.\n"
f"Sentence 2: What the next stage {next_stage_label} will do.\n\n"
f"Do not mention artifacts. Do not mention approvals. Do not use phrases like "
f"'the user should'. Do not pad with filler sentences. Just 2 clean sentences.\n\n"
f"Respond in this exact JSON format:\n"
f'{{"summary": "...", "next_stage_preview": "..."}}'
)
try:
from crewai import LLM
result = llm.call(messages=[{"role": "user", "content": prompt}])
# Parse the response β try to extract JSON
text = str(result) if result else ""
# Try to find JSON in the response
import re
json_match = re.search(r'\{[^{}]*"summary"[^{}]*"next_stage_preview"[^{}]*\}', text, re.DOTALL)
if json_match:
try:
parsed = json.loads(json_match.group())
return parsed
except json.JSONDecodeError:
pass
# Fallback: use the text as summary
return {
"summary": text[:500] if text else f"Completed {stage_name} stage for {design_name}.",
"next_stage_preview": f"Next: {next_stage} β {next_stage_desc}" if next_stage else "Build complete."
}
except Exception as e:
logger.warning(f"LLM summary generation failed: {e}")
# Deterministic fallback β keep it to 2 sentences max
next_desc_short = STAGE_DESCRIPTIONS.get(next_stage, "") if next_stage else ""
return {
"summary": (
f"{stage_name.replace('_', ' ').title()} completed for {design_name}. "
f"{'Next up: ' + next_stage.replace('_', ' ').title() + '.' if next_stage else 'Build complete.'}"
),
"next_stage_preview": (
f"{next_desc_short}" if next_stage
else "Build complete β all stages finished."
)
}
def build_stage_complete_payload(orchestrator, stage_name: str, design_name: str, llm) -> dict:
"""Build the complete stage_complete event payload."""
artifacts = collect_stage_artifacts(orchestrator, stage_name)
decisions = collect_stage_decisions(orchestrator, stage_name)
warnings = collect_stage_warnings(orchestrator, stage_name)
stage_log = get_stage_log_summary(orchestrator, stage_name)
next_stage = get_next_stage(stage_name)
# Generate LLM summary
llm_result = generate_stage_summary_llm(
llm=llm,
stage_name=stage_name,
design_name=design_name,
stage_log=stage_log,
artifacts=artifacts,
decisions=decisions,
next_stage=next_stage,
)
return {
"type": "stage_complete",
"stage_name": stage_name,
"summary": llm_result.get("summary", ""),
"artifacts": artifacts,
"decisions": decisions,
"warnings": warnings,
"next_stage_name": next_stage or "DONE",
"next_stage_preview": llm_result.get("next_stage_preview", ""),
"timestamp": time.time(),
}
# βββ Human-readable stage name mapping βββββββββββββββββββββββββββββββ
STAGE_HUMAN_NAMES = {
"INIT": "Initialization",
"SPEC": "Architecture Specification",
"SPEC_VALIDATE": "Specification Validation",
"HIERARCHY_EXPAND": "Hierarchy Expansion",
"FEASIBILITY_CHECK": "Feasibility Check",
"CDC_ANALYZE": "CDC Analysis",
"VERIFICATION_PLAN": "Verification Planning",
"RTL_GEN": "RTL Generation",
"RTL_FIX": "RTL Syntax Fixing",
"VERIFICATION": "Verification",
"FORMAL_VERIFY": "Formal Verification",
"COVERAGE_CHECK": "Coverage Analysis",
"REGRESSION": "Regression Testing",
"SDC_GEN": "SDC Generation",
"FLOORPLAN": "Floorplanning",
"HARDENING": "GDSII Hardening",
"CONVERGENCE_REVIEW": "Convergence Review",
"ECO_PATCH": "ECO Patch",
"SIGNOFF": "Signoff",
"SUCCESS": "Build Complete",
"FAIL": "Build Failed",
}
def generate_failure_explanation(llm, stage_name: str, design_name: str,
error_log: str) -> dict:
"""Generate a calm, human-readable explanation of what went wrong.
Returns: {"explanation": str, "suggestion": str}
"""
human_stage = STAGE_HUMAN_NAMES.get(stage_name, stage_name.replace("_", " ").title())
prompt = (
f"A chip design build for '{design_name}' stopped at the {human_stage} stage.\n\n"
f"Error log (last entries):\n{error_log[:2000]}\n\n"
f"In 1-2 sentences, explain what went wrong in plain language a hardware engineer "
f"would understand. Do not be alarmist. Be specific about the actual error.\n\n"
f"Then in one sentence, suggest one specific thing the user could try differently "
f"in their chip description to avoid this issue.\n\n"
f"Respond in this exact JSON format:\n"
f'{{"explanation": "...", "suggestion": "..."}}'
)
try:
result = llm.call(messages=[{"role": "user", "content": prompt}])
text = str(result) if result else ""
import re
json_match = re.search(r'\{[^{}]*"explanation"[^{}]*"suggestion"[^{}]*\}', text, re.DOTALL)
if json_match:
try:
parsed = json.loads(json_match.group())
return parsed
except json.JSONDecodeError:
pass
return {
"explanation": text[:300] if text else f"The build stopped during {human_stage}.",
"suggestion": "Try simplifying the design or checking the error log for details."
}
except Exception as e:
logger.warning(f"Failure explanation generation failed: {e}")
return {
"explanation": f"The build stopped during {human_stage}. Check the log for specific errors.",
"suggestion": "Try simplifying the design description or reducing complexity."
}
|