Sai Kumar Taraka
Initial commit: UVM testbench generator with coverage-driven auto-training
4344b33
# src/main.py — CLI entry point
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from src.pipeline import TBPipeline
def create_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="UVM TB Generator — ML-style pipeline with coverage-driven auto-training",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python -m src.main --spec configs/uart16550-1.5.core
python -m src.main --spec configs/uart_demo.yaml --auto-train --max-iterations 10
python -m src.main --spec configs/uart_demo.yaml --simulator icarus
python -m src.main --spec configs/uart_demo.yaml --pipeline-config configs/base_config.yaml --output-dir my_tbs
python -m src.main --spec configs/uart_demo.yaml --eval-only
""",
)
parser.add_argument("--spec", required=True, help="Design spec YAML/.core/JSON path")
parser.add_argument("--pipeline-config", default=None, help="Pipeline config YAML path")
parser.add_argument("--output-dir", default=None, help="Override output directory")
parser.add_argument("--eval-only", action="store_true", help="Only evaluate (no generation)")
parser.add_argument("--log-level", default=None, choices=["DEBUG", "INFO", "WARNING", "ERROR"])
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--auto-train", action="store_true", help="Enable coverage-driven auto-training loop")
parser.add_argument("--max-iterations", type=int, default=5, help="Max auto-training iterations (default: 5)")
parser.add_argument("--coverage-target", type=float, default=90.0, help="Coverage target %% (default: 90)")
parser.add_argument("--simulator", default="stub", choices=["stub", "icarus", "vcs", "questa"],
help="Simulator backend (default: stub)")
return parser
def main() -> None:
parser = create_parser()
args = parser.parse_args()
if not Path(args.spec).exists():
print(f"ERROR: Spec file not found: {args.spec}", file=sys.stderr)
sys.exit(1)
pipeline = TBPipeline()
if args.output_dir:
pipeline.cfg.generation.output_dir = args.output_dir
if args.log_level:
pipeline.cfg.logging.level = args.log_level
if args.auto_train:
pipeline.cfg.auto_train.enabled = True
pipeline.cfg.auto_train.max_iterations = args.max_iterations
pipeline.cfg.auto_train.coverage_target = args.coverage_target
pipeline.cfg.auto_train.simulator = args.simulator
if args.eval_only:
from src.config import ConfigLoader
from src.data.validators import SpecValidator
from src.evaluation.metrics import TBMetrics
from src.evaluation.reporters import Reporter, Report
from src.features.extractors import SpecFeatureExtractor
loader = ConfigLoader()
spec, _ = loader.load(args.spec)
validator = SpecValidator()
vr = validator.validate(spec)
if not vr:
print(vr)
sys.exit(1)
extractor = SpecFeatureExtractor()
features = extractor.extract(spec)
print(f"Features: {features.model_dump_json(indent=2)}")
metrics = TBMetrics().evaluate_all(spec, list(TemplateModel.TEMPLATE_MAP.keys()))
report = Report(metrics, spec.design_name, all(v >= 0.7 for v in metrics.values()))
Reporter().report(report)
return
try:
result = pipeline.run(args.spec, args.pipeline_config)
except Exception as e:
print(f"Pipeline failed: {e}", file=sys.stderr)
sys.exit(1)
if args.json:
print(json.dumps(result, indent=2, default=str))
else:
ev = result["evaluation"]
print(f"\n{'='*60}")
print(f"Design: {result['design_name']}")
print(f"Status: {'PASS' if result['passed'] else 'FAIL'}")
print(f"Simulator: {result['simulator']}")
print(f"Versions: {result.get('all_versions', [])}")
print(f"Latest ver: {result['model_version']}")
print(f"Iterations: {result['auto_train_iterations']}")
print(f"--- Metrics ---")
for k, v in ev.items():
if isinstance(v, float):
print(f" {k}: {v:.2%}" if v <= 1.0 else f" {k}: {v:.1f}")
else:
print(f" {k}: {v}")
if result.get("coverage_trend"):
print(f"--- Coverage Trend ---")
for ver, cov in result["coverage_trend"]:
bar = "#" * int(cov / 5)
print(f" {ver}: {cov:5.1f}% |{bar}")
if result.get("coverage_analysis"):
ca = result["coverage_analysis"]
print(f"--- Coverage Analysis ---")
print(f" Bins: {ca['covered_bins']}/{ca['total_bins']} ({ca['coverage_pct']:.1f}%)")
if ca["gaps"]:
print(f" Gaps:")
for g in ca["gaps"]:
print(f" - {g['bin']} (addr={g['addr']}, dir={g['dir']})")
if result.get("version_comparison"):
vc = result["version_comparison"]
print(f"--- Version Delta ---")
for k, d in vc.get("metric_deltas", {}).items():
arrow = "+" if d["delta"] > 0 else ("-" if d["delta"] < 0 else "=")
print(f" {k}: {d['from']:.2f} {arrow} {d['to']:.2f} ({d['delta']:+.2f})")
print(f"{'='*60}")
if __name__ == "__main__":
main()