| import React from 'react'; | |
| import { motion } from 'framer-motion'; | |
| import { API_BASE } from '../api'; | |
| interface Props { | |
| designName: string; | |
| result: any; | |
| jobStatus: string; | |
| events: any[]; | |
| jobId?: string; | |
| onReset: () => void; | |
| } | |
| function StatCell({ label, value, warn }: { label: string; value: any; warn?: boolean }) { | |
| return ( | |
| <div className="cs-stat"> | |
| <span className={`cs-stat-value${warn ? ' cs-stat-value--warn' : ''}`}> | |
| {value ?? 'β'} | |
| </span> | |
| <span className="cs-stat-label">{label}</span> | |
| </div> | |
| ); | |
| } | |
| function CheckRow({ label, passed, detail }: { label: string; passed: boolean | null; detail?: string }) { | |
| const status = passed === true ? 'pass' : passed === false ? 'fail' : 'skip'; | |
| return ( | |
| <div className="cs-check-row"> | |
| <span className={`cs-check-icon cs-check-icon--${status}`}> | |
| {status === 'pass' ? 'β' : status === 'fail' ? 'β' : 'β'} | |
| </span> | |
| <div className="cs-check-body"> | |
| <span className="cs-check-label">{label}</span> | |
| {detail && <span className="cs-check-detail">{detail}</span>} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export const ChipSummary: React.FC<Props> = ({ designName, result, jobStatus, events, jobId, onReset }) => { | |
| const success = jobStatus === 'done'; | |
| const metrics = result?.metrics || {}; | |
| const convergence = result?.convergence_history || []; | |
| const spec = result?.spec || ''; | |
| const rtlSnippet = result?.rtl_snippet || ''; | |
| const strategy = result?.strategy || ''; | |
| const buildTimeSec = result?.build_time_s || 0; | |
| const buildTimeMin = Math.round(buildTimeSec / 60); | |
| const coverage = result?.coverage || {}; | |
| const formalResult = result?.formal_result || ''; | |
| const signoffResult = result?.signoff_result || ''; | |
| const selfHeal = result?.self_heal || { | |
| stage_exception_count: events.filter(e => /stage .* exception/i.test(e?.message || '')).length, | |
| formal_regen_count: events.filter(e => /regenerating sva/i.test(e?.message || '')).length, | |
| coverage_best_restore_count: events.filter(e => /restoring best testbench/i.test(e?.message || '')).length, | |
| coverage_regression_reject_count: events.filter(e => /regressed coverage/i.test(e?.message || '')).length, | |
| deterministic_tb_fallback_count: events.filter(e => /deterministic tb fallback/i.test(e?.message || '')).length, | |
| }; | |
| const checkpointCount = events.filter(e => e.type === 'transition' || e.type === 'checkpoint').length; | |
| const syntaxPassed = events.some(e => /syntax.*pass|lint.*clean/i.test(e?.message || '')); | |
| const simPassed = events.some(e => /test passed|simulation.*pass/i.test(e?.message || '')); | |
| const formalPassed = formalResult ? /pass|success/i.test(formalResult) : events.some(e => /formal.*pass/i.test(e?.message || '')); | |
| const coveragePassed = events.some(e => /coverage passed/i.test(e?.message || '')); | |
| const signoffPassed = signoffResult ? /pass|success/i.test(signoffResult) : null; | |
| const lineCov = coverage?.line || coverage?.line_pct; | |
| const branchCov = coverage?.branch || coverage?.branch_pct; | |
| const totalHealActions = | |
| (selfHeal.stage_exception_count || 0) + | |
| (selfHeal.formal_regen_count || 0) + | |
| (selfHeal.coverage_best_restore_count || 0) + | |
| (selfHeal.coverage_regression_reject_count || 0) + | |
| (selfHeal.deterministic_tb_fallback_count || 0); | |
| const hasMetrics = metrics.wns !== undefined || metrics.area || metrics.power || metrics.gate_count; | |
| const failureExplanation = result?.failure_explanation; | |
| const failureSuggestion = result?.failure_suggestion; | |
| return ( | |
| <div className="cs-root"> | |
| {/* ββ Banner βββββββββββββββββββββββββββββββ */} | |
| <motion.div | |
| className={`cs-banner ${success ? 'cs-banner--success' : 'cs-banner--fail'}`} | |
| initial={{ opacity: 0, y: -8 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.35 }} | |
| > | |
| <div className="cs-banner-dot" /> | |
| <div className="cs-banner-body"> | |
| <h1 className="cs-banner-title"> | |
| {success ? `${designName} is ready` : 'Build stopped'} | |
| </h1> | |
| <p className="cs-banner-sub"> | |
| {success | |
| ? [ | |
| buildTimeMin > 0 ? `${buildTimeMin} min` : null, | |
| checkpointCount > 0 ? `${checkpointCount} checkpoints passed` : null, | |
| strategy || null, | |
| ].filter(Boolean).join(' Β· ') | |
| : `${designName} Β· Review the log below for details` | |
| } | |
| </p> | |
| </div> | |
| </motion.div> | |
| {/* ββ Silicon Metrics βββββββββββββββββββββββ */} | |
| {success && hasMetrics && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">Silicon Metrics</h2> | |
| <div className="cs-stats-row"> | |
| <StatCell | |
| label="Worst negative slack" | |
| value={metrics.wns !== undefined ? `${metrics.wns} ns` : null} | |
| warn={metrics.wns !== undefined && metrics.wns < 0} | |
| /> | |
| <div className="cs-stat-divider" /> | |
| <StatCell label="Die area" value={metrics.area} /> | |
| <div className="cs-stat-divider" /> | |
| <StatCell label="Total power" value={metrics.power} /> | |
| <div className="cs-stat-divider" /> | |
| <StatCell label="Gate count" value={metrics.gate_count} /> | |
| </div> | |
| </div> | |
| )} | |
| {/* ββ Verification ββββββββββββββββββββββββββ */} | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">Verification</h2> | |
| <div className="cs-checks"> | |
| <CheckRow | |
| label="RTL syntax & lint" | |
| passed={syntaxPassed || null} | |
| detail={syntaxPassed ? 'Clean β Verilator lint-only' : undefined} | |
| /> | |
| <CheckRow | |
| label="Functional simulation" | |
| passed={simPassed || null} | |
| detail={simPassed ? 'TEST PASSED' : undefined} | |
| /> | |
| <CheckRow | |
| label="Formal property verification" | |
| passed={formalPassed} | |
| detail={formalResult ? formalResult.substring(0, 60) : undefined} | |
| /> | |
| <CheckRow | |
| label="Coverage closure" | |
| passed={coveragePassed || null} | |
| detail={lineCov ? `Line ${lineCov}%${branchCov ? ` Β· Branch ${branchCov}%` : ''}` : undefined} | |
| /> | |
| <CheckRow | |
| label="DRC / LVS signoff" | |
| passed={signoffPassed} | |
| detail={signoffResult ? signoffResult.substring(0, 60) : (success ? 'Physical checks completed' : undefined)} | |
| /> | |
| </div> | |
| </div> | |
| {/* ββ Self-Healing ββββββββββββββββββββββββββ */} | |
| {totalHealActions > 0 && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">Self-Healing Activity</h2> | |
| <div className="cs-heal-list"> | |
| {selfHeal.stage_exception_count > 0 && ( | |
| <div className="cs-heal-item"> | |
| Stage exception guards | |
| <span className="cs-heal-count">{selfHeal.stage_exception_count}Γ</span> | |
| </div> | |
| )} | |
| {selfHeal.formal_regen_count > 0 && ( | |
| <div className="cs-heal-item"> | |
| Formal SVA regeneration | |
| <span className="cs-heal-count">{selfHeal.formal_regen_count}Γ</span> | |
| </div> | |
| )} | |
| {selfHeal.coverage_regression_reject_count > 0 && ( | |
| <div className="cs-heal-item"> | |
| Testbench regression rejections | |
| <span className="cs-heal-count">{selfHeal.coverage_regression_reject_count}Γ</span> | |
| </div> | |
| )} | |
| {selfHeal.coverage_best_restore_count > 0 && ( | |
| <div className="cs-heal-item"> | |
| Best testbench restores | |
| <span className="cs-heal-count">{selfHeal.coverage_best_restore_count}Γ</span> | |
| </div> | |
| )} | |
| {selfHeal.deterministic_tb_fallback_count > 0 && ( | |
| <div className="cs-heal-item"> | |
| Deterministic testbench fallbacks | |
| <span className="cs-heal-count">{selfHeal.deterministic_tb_fallback_count}Γ</span> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| )} | |
| {/* ββ Architecture Spec βββββββββββββββββββββ */} | |
| {spec && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">Architecture Specification</h2> | |
| <div className="cs-code-block"> | |
| <pre className="cs-pre cs-pre--prose"> | |
| {spec.substring(0, 1200)}{spec.length > 1200 ? '\nβ¦' : ''} | |
| </pre> | |
| </div> | |
| </div> | |
| )} | |
| {/* ββ RTL Preview βββββββββββββββββββββββββββ */} | |
| {rtlSnippet && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">RTL Preview</h2> | |
| <div className="cs-code-block cs-code-block--dark"> | |
| <pre className="cs-pre cs-pre--code"> | |
| {rtlSnippet.substring(0, 1200)}{rtlSnippet.length > 1200 ? '\n// β¦' : ''} | |
| </pre> | |
| </div> | |
| </div> | |
| )} | |
| {/* ββ Convergence History βββββββββββββββββββ */} | |
| {convergence.length > 0 && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">Convergence History</h2> | |
| <div className="cs-table-wrap"> | |
| <table className="cs-table"> | |
| <thead> | |
| <tr> | |
| <th>Iter</th><th>WNS (ns)</th><th>TNS (ns)</th> | |
| <th>Congestion %</th><th>Area (Β΅mΒ²)</th><th>Power (W)</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {convergence.slice(-5).map((s: any, i: number) => ( | |
| <tr key={i}> | |
| <td>{s.iteration}</td> | |
| <td data-good={s.wns >= 0 ? 'true' : 'false'}>{s.wns?.toFixed(3)}</td> | |
| <td>{s.tns?.toFixed(3)}</td> | |
| <td>{s.congestion?.toFixed(2)}</td> | |
| <td>{s.area_um2?.toFixed(0)}</td> | |
| <td>{s.power_w?.toExponential(2)}</td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| )} | |
| {/* ββ Failure Detail ββββββββββββββββββββββββ */} | |
| {!success && (failureExplanation || result?.error) && ( | |
| <div className="cs-section"> | |
| <h2 className="cs-section-title">What went wrong</h2> | |
| {failureExplanation && ( | |
| <p className="cs-fail-explanation">{failureExplanation}</p> | |
| )} | |
| {failureSuggestion && ( | |
| <p className="cs-fail-suggestion"> | |
| <span className="cs-fail-suggestion-label">Try:</span> | |
| {failureSuggestion} | |
| </p> | |
| )} | |
| {result?.error && !failureExplanation && ( | |
| <div className="cs-code-block"> | |
| <pre className="cs-pre cs-pre--error"> | |
| {String(result.error).substring(0, 500)} | |
| </pre> | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| {/* ββ Actions βββββββββββββββββββββββββββββββ */} | |
| <div className="cs-actions"> | |
| {/* Full build report downloads */} | |
| {jobId && ( | |
| <div className="cs-report-downloads"> | |
| <span className="cs-report-label">Download Build Report:</span> | |
| <a | |
| className="cs-report-btn" | |
| href={`${API_BASE}/report/${jobId}/full.pdf`} | |
| download | |
| title="Download full build report as PDF" | |
| > | |
| β PDF Report | |
| </a> | |
| <a | |
| className="cs-report-btn" | |
| href={`${API_BASE}/report/${jobId}/full.docx`} | |
| download | |
| title="Download full build report as DOCX" | |
| > | |
| β DOCX Report | |
| </a> | |
| </div> | |
| )} | |
| <motion.button | |
| className="cs-reset-btn" | |
| onClick={onReset} | |
| whileHover={{ scale: 1.02 }} | |
| whileTap={{ scale: 0.98 }} | |
| > | |
| Build another chip | |
| </motion.button> | |
| </div> | |
| </div> | |
| ); | |
| }; | |