| |
|
|
| 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() |
|
|