Skip to content

Latest commit

 

History

History
421 lines (349 loc) · 24 KB

File metadata and controls

421 lines (349 loc) · 24 KB

CloseLoop AI — Product Requirements Document (PRD)

One-liner: "CloseLoop AI is a multi-agent chip-design copilot that automatically finds timing violations, recommends engineering fixes, simulates timing/power tradeoffs, and (aspirationally) learns from previous successful fixes using Redis."

Tagline: Cursor for chip timing closure.

Built for WeaveHacks 4 (June 2026). Stack: Next.js 14 (App Router) + TypeScript + React Flow + Zustand + Tailwind. Runs 100% offline in mock mode with zero API keys.

This document is reverse-engineered from the actual source tree, file by file and function by function, so the team has a precise, shared mental model of what the system does today, what the agents are, and where the pitch is aspirational vs. implemented.


1. Problem & Vision

1.1 The problem

Chip physical-design timing closure is one of the slowest steps in semiconductor development — traditionally weeks to months. Today the loop is fully manual:

Engineer → reads huge STA report → finds violation → guesses a fix →
re-runs the tool → checks if it worked → repeats for days

1.2 The CloseLoop vision

Replace that manual loop with an autonomous, self-correcting team of agents:

STA Agent (finds violation)
   → Fix Agent (suggests ECO fix)
      → PPA Agent (checks power/area impact)
         → Orchestrator (applies fix, recomputes timing, decides stop/continue)
            → Redis (stores the learning for reuse)

The differentiators claimed in the pitch:

  1. Open-source
  2. ASIC-targeted (vs. Nexus which is FPGA-only)
  3. Self-correcting (orchestrator detects diminishing returns and hard-stops)
  4. Live visualization (the only system with a visual sandbox)
  5. Engineering memory — accumulate problem→fix→result experience in Redis and reuse it (described as the strongest sponsor story).

⚠️ Reality check (see §11): items 1–4 are implemented. Item 5 (engineering memory / learning / ranking / reuse) is scaffolded but not yet wired — the Redis keys exist but no read/rank/retrieve logic uses them. This is the single most important gap to close.


2. System Architecture

┌───────────────────────────────────────────────────────────┐
│  Next.js 14 App Router (TypeScript)                         │
│  ┌──────────────┐        ┌──────────────────┐               │
│  │ Dashboard UI │ ◄────► │ Zustand store    │               │
│  │ (React Flow) │        │ (useDesignStore) │               │
│  └──────┬───────┘        └──────────────────┘               │
│         │ fetch (lib/clientApi.ts)                           │
│  ┌──────▼─────────────────────────────────────┐            │
│  │  app/api/*  route handlers (9 endpoints)    │            │
│  └──────┬─────────────────────────────────────┘            │
│         │                                                    │
│  ┌──────▼─────────────────────────────────────┐            │
│  │  lib/apiRouter.ts — switches mock ↔ real    │            │
│  └──┬──────────────┬─────────────────┬─────────┘            │
│     ▼              ▼                 ▼                       │
│  Agents         Redis             Weave                      │
│ (5 mock or    (in-mem Map or    (no-op logger or            │
│  GPT-4o)      real ioredis)      real W&B)                   │
│     │                                                        │
│  ┌──▼──────────────────────────────┐                        │
│  │ lib/timing/* — deterministic     │  ← the "physics"      │
│  │ timing engine (formulas)         │     ground truth      │
│  └─────────────────────────────────┘                        │
└───────────────────────────────────────────────────────────┘

Key design principle: All agent outputs are grounded by a deterministic timing engine (lib/timing/timingEngine.ts). Agents narrate and recommend; the engine computes the actual slack from physical formulas every time a fix is applied. This is why it is "not a fake hardcoded demo" — applying a fix re-runs the formulas.

2.1 Mock vs. Real mode

lib/apiRouter.ts is the central switchboard:

  • isMockMode() → mock unless NEXT_PUBLIC_MOCK_MODE=false and OPENAI_API_KEY is set.
  • getAgentClient() → mock agents or (placeholder) real OpenAI agents. Today both branches return the mock agent functions; the real branch only flips the isMock flag. Real GPT-4o wiring is a TODO with a clean upgrade path.
  • getRedisClient() → real ioredis if REDIS_URL set, else in-memory MockRedis.
  • getWeaveClient() → currently always returns MockWeaveClient (real W&B is a stub).

So today the app is effectively mock-only for agents & Weave, with optional real Redis.


3. The Agents (the heart of the orchestration)

There are 5 agent types (AgentType in lib/types.ts): STA, FIX, PPA, ORCHESTRATOR, EXPLAINABILITY. Each has:

  • A wrapper in lib/agents/<name>Agent.ts that adds Weave tracing + audit logging and shapes the result into a uniform AgentOutput<TInput, TOutput>.
  • A mock implementation in lib/mock/mockAgents.ts containing the actual logic.
  • A system prompt in lib/agents/prompts.ts (used when/if real LLM mode is wired).

Every agent call produces an AgentOutput with: agentType, timestamp, input, output, reasoningSteps[], latencyMs, isMock. Mock agents simulate realistic latency (simulateLatency, 400–900ms) so the timeline feels alive.

3.1 STA Agent — "Finds the violation"

  • Wrapper: lib/agents/staAgent.tsrunSTAAgent(design).
  • Logic: mockSTAAgent(design) in lib/mock/mockAgents.ts.
  • Input: full DesignState.
  • Output schema: STAAgentOutput:
    • worst_path: string[], slack, wns, tns, required_time, arrival_time
    • cell_delays: {cellId: ns}, wire_delays: {netId: ns}
    • root_causes: RootCause[], root_cause_analysis: {cause: explanation}
    • plain_english, severity (critical|warning|marginal), recommended_priority.
  • What it actually does:
    1. Calls findWorstPath(design) (timing engine) to get the worst path + per-cell/per-net delays.
    2. Builds cell_delays / wire_delays maps.
    3. Generates human-readable root_cause_analysis strings for each detected cause (e.g. "NET_LONG_1 is 210um — contributes 0.42ns, 42% of the clock period").
    4. Classifies severity by slack (< -0.05 = critical, < 0 = warning).
    5. Writes a plain-English diagnosis.
  • Root causes detected (RootCause): long_net_delay, weak_buffer, high_fanout, logic_depth, clock_skew, setup_violation (last two are typed but not actively detected by the mock).

3.2 Fix Agent — "Suggests the ECO fix"

  • Wrapper: lib/agents/fixAgent.tsrunFixAgent(staOutput, design).
  • Logic: mockFixAgent(staOutput, design).
  • Output schema: FixAgentOutput = { fixes: Fix[], recommendedOrder: string[], stopCondition: string }.
  • Fix types (FixType): upsize_cell, insert_buffer, move_closer, restructure_logic, add_repeater.
  • What it actually does (rule-based heuristics today):
    1. Weak buffer → upsize: if a BUF with driveStrength ≤ 1 is on the worst path, emit FIX_UPSIZE_1 (e.g. BUF_X1 → BUF_X4), est. +0.09ns, +3.2% power, low risk.
    2. Long net → buffer insertion: the longest critical net with length > 150um gets FIX_BUF_1 (insert BUF_X2 at midpoint, splits the wire), est. +0.17ns, +1.8% power.
    3. Fallback: if neither, move_closer to tighten margin (+0.02ns).
    4. Each fix's ecoBudget is recomputed via checkECOLegality (routability scoring).
    5. Orders fixes by estimatedSlackImprovement desc; writes a stopCondition summary.
  • Each Fix carries: type, target, from/to, targetNet, bufferType, reason, estimatedSlackImprovement, estimatedPowerDelta, estimatedAreaDelta, riskLevel, ecoBudget, plainEnglish.

3.3 PPA Agent — "Checks power/area impact"

  • Wrapper: lib/agents/ppaAgent.tsrunPPAAgent(fixes, design).
  • Logic: mockPPAAgent(fixes, design).
  • Output schema: PPAAgentOutput = { perFixImpact[], combinedImpact, overallRecommendation, plainEnglish }.
  • What it actually does:
    1. For each fix, decides a verdict (accept | modify | reject): good tradeoff if slackImprovement > 0.03 and powerDelta < slackImprovement × 50; flags modify if powerDelta > 10%.
    2. Sums per-fix deltas into combinedImpact (timing/power/area/congestion).
    3. Projects expectedAfterSlack = design.wns + combinedTimingDelta and writes an overall recommendation ("Accept all fixes. Timing improves from X to +Y (closure achieved)…").

3.4 Orchestrator Agent — "Runs the loop, decides when to stop"

  • Wrapper: lib/agents/orchestratorAgent.tsrunOrchestrator(design, iterations).
  • Logic: mockOrchestratorAgent(design, iterations).
  • Output schema: OrchestratorOutput = { nextAction, reason, closureAchieved, iterationSummary, stopReason }; nextAction ∈ {run_sta, run_fix, apply_fix, stop}.
  • Stop rules (hard constraints):
    • Closure achieved when WNS ≥ +0.02ns → stop.
    • Max 5 iterations → stop (prevents infinite loops).
    • Diminishing returns: if the last 2 iterations each improved < 0.01ns → stop.
  • Full loop: mockRunClosureLoop(initialDesign, onIteration?) ties everything together: recompute → orchestrator → STA → Fix → applyFixesToDesign → record Iteration → repeat (≤5). This is what the "Run Full Closure" button triggers.

3.5 Explainability Agent — "Translates to plain English"

  • Wrapper: lib/agents/explainabilityAgent.tsrunExplainabilityAgent(before, after).
  • Logic: mockExplainabilityAgent(before, after).
  • Output schema: ExplainabilityOutput = { beforeStory, afterStory, analogy, businessImpact, oneLiner }.
  • What it does: turns before/after slack & power into relay-race analogies and a business impact statement (e.g. "runs at target 1GHz → faster inference, better battery life").
  • Note: the wrapper/agent exists and is fully functional, but the dashboard does not currently invoke it via an API route — the Judge Mode overlay hardcodes equivalent narration. (Easy win: wire it through.)

4. Orchestration / Data Flow

4.1 Single-step flow (manual demo)

[Run STA button] → POST /api/sta/analyze
   → runSTAAgent → mockSTAAgent → STAAgentOutput (timeline entry pushed)
   → (auto) POST /api/fix/recommend
        → runFixAgent (Fix[]) + runPPAAgent (verdicts)  → fix cards + PPA panel
[Apply Fix button] → POST /api/fix/apply
   → applyFixesToDesign(design, fixes)  [timing engine recomputes]
   → store new design + iteration in Redis; append audit events
   → red path turns green if WNS ≥ 0

4.2 Full-closure flow (autonomous)

[Run Full Closure] → POST /api/closure/run
   → getAgentClient().closureLoop(design) = mockRunClosureLoop
   → loops Orchestrator → STA → Fix → apply (≤5 iterations)
   → persists finalDesign + iteration history to Redis
   → UI synthesizes one timeline entry per iteration

4.3 Client orchestration

app/page.tsx (the Dashboard) is the front-end conductor. It uses lib/clientApi.ts (thin fetch wrappers) to hit the API routes and writes results into the Zustand store (lib/store/useDesignStore.ts). On mount it auto-loads the demo design and benchmarks.


5. The Timing Engine (deterministic "physics")

lib/timing/timingEngine.ts is the ground truth that prevents the demo from being fake.

  • Cell delay: cellDelay(cell, fanout) = baseDelay / sqrt(driveStrength) + max(0, fanout-2)*0.03. → Upsizing a cell (higher drive strength) reduces its delay.
  • Wire delay (cubic RC model): wireDelay(net) = (length/100)³ × 0.05. → Long wires hurt disproportionately; tuned so 210um ≈ 0.46ns and 105um ≈ 0.06ns. This makes buffer insertion physically meaningful — splitting a 210um wire into two 105um halves + a ~0.09ns buffer is a net win of ~0.25ns.
  • Path arrival / slack: pathArrival = ΣcellDelay + ΣwireDelay + setupTime; slack = requiredTime − arrivalTime (negative = violation).
  • Fix application functions: applyUpsize (delay↓, power/area↑ log-scaled), applyBufferInsertion (splits a net + inserts buffer cell at midpoint), applyMoveCloser (reduces net length %).
  • findWorstPath: orders cells along the critical path via net topology (orderCellsAlongPath / orderNetsAlongPath) and detects root causes (detectRootCauses: long net > 150um, weak buffer, fanout ≥ 4, logic depth > 6 cells).
  • recomputeDesign: the master recompute — recalculates worst path, WNS, TNS (tns ≈ wns × 2.2 when negative), PPA, per-cell timing status, MCMM corners. Called after every fix.
  • applyFixesToDesign: applies a list of Fix objects to the design, then recomputes.
  • MCMM: computeMCMM applies PVT derating per corner (process ss=×1.15 / ff=×0.9, voltage 0.9/V, temp 1+(T−25)×0.001) → identifies the worst-case ss_0p9v_125c corner.
  • Signoff: applySignoffMargin(slack, 5%) — tighter signoff requirement; a path can pass STA but fail signoff margin (flagged in the MCMM panel).
  • PPA: computePPA sums cell power/area and derives congestion from area + total wire length.

lib/timing/ecoBudget.tsbuildECOReport: per-fix routability scoring; flags warnings if routabilityScore < 50 and illegal if < 30.

lib/timing/reportParser.tsparseOpenSTAReport + reportToTimingPath: lets a user paste a real OpenSTA timing report (regex-based parser for Startpoint/Endpoint/arrival/required/ slack + per-cell/per-net lines) and convert it into the internal TimingPath model. Exposed via POST /api/report/parse.


6. Data Model (lib/types.ts)

Core entities:

  • Cell — id, type (FF|BUF|NAND|INV|AND|OR|MUX), driveStrength, baseDelay, power, area, x/y (layout), label, isOnCriticalPath, timingStatus, block (hierarchy: ALU/Control).
  • Net — id, source, target, length(um), fanout, baseWireDelay, isOnCriticalPath.
  • TimingPath — startpoint/endpoint, required/arrival/slack, ordered cells+delays, ordered nets+delays, setupTime, rootCauses.
  • DesignState — name, technology, cells[], nets[], worstPath, wns, tns, power, area, congestion, corners[], iteration, setupTime, requiredTime, clockPeriod.
  • Fix, ECOBudget, Iteration, Corner, BenchmarkResult, AuditEvent, plus the agent I/O schemas (STAAgentOutput, FixAgentOutput, PPAAgentOutput, OrchestratorOutput, ExplainabilityOutput).

6.1 Demo design (data/demo_design.json)

An 8-cell ALU datapath in nangate45 ASIC tech, 1.0ns clock period:

  • Critical path: FF1 → NAND_X1 → BUF_X1 (weak) → INV_X1 → FF2.
  • The smoking gun: NET_LONG_1 = 210um wire from BUF_X1 → INV_X1 (cubic delay penalty), driven by a weak BUF_X1 (driveStrength 1).
  • Side branch (Control block): NAND_X1 → AND_X2 → BUF_X2_side → OR_X1 → FF2.
  • 3 MCMM corners; worst-case = ss_0p9v_125c.
  • Initial WNS ≈ −0.21 to −0.23ns (violation); after fixes ≈ +0.05 to +0.15ns (closed).

7. API Surface (app/api/*)

Route Method Purpose
/api/design/demo GET Load demo design, persist to Redis, reset iterations
/api/design/current GET/POST Read/write current design state
/api/sta/analyze POST Run STA agent on a design
/api/fix/recommend POST Run Fix agent + PPA agent together
/api/fix/apply POST Apply selected fixes, recompute, record an iteration
/api/closure/run POST Run the full autonomous closure loop
/api/iterations GET Fetch iteration history from Redis
/api/benchmark GET Benchmark comparison (updates "ours" row live)
/api/mcmm GET MCMM corner slacks with PVT derating
/api/report/parse POST Parse a pasted OpenSTA report → TimingPath

All routes return a uniform envelope: { ok, isMock, data?, error?, latencyMs }.


8. UI / Components

app/page.tsx lays out the dashboard. Top bar actions: Judge Mode, Export, Reset Demo, Run STA, Run Full Closure.

Component Role
ChipGraph/* React Flow graph of the chip. CellNode, NetEdge, PathAnimator/ConfettiBurst. Critical edges colored red/amber/green by status; animated signal flow on the critical path.
TimingPanel/* SlackGauge (−0.5↔+0.5ns gauge), PathTable (cell-by-cell delay breakdown), PPAChart (WNS/TNS/power trend over iterations), plus the STA agent's plain-English explanation + root causes.
MCMMPanel Corner selector with PVT derating, signoff-margin correlation, "passes STA but fails signoff" warning.
FixCards/* Selectable ECO fix cards from the Fix Agent + combined-impact summary + PPA verdict. Apply button.
AgentTimeline/* Live feed of agent steps (AgentBadge, AgentStep) with latency + mock badges; shows the running stage.
BenchmarkPanel/* ComparisonTable: CloseLoop vs Nexus vs ChatEDA vs Manual.
JudgeMode/* 5-step auto-advancing non-technical walkthrough (StoryStep). The judge-facing narrative.
CopilotChat Chat sidebar ("CopilotKit") with canned NL responses + action buttons (run STA, apply fixes, judge mode, run closure).
ui/* Badge, Button, Card, StatusBar primitives.

8.1 Judge Mode (the demo story — 5 steps)

  1. The Problem — red critical path, WNS ≈ −0.21ns, signal arrives late.
  2. The AI Agent Found It — STA agent diagnosis (~750ms), root causes.
  3. It Recommended Two Fixes — upsize BUF_X1 → BUF_X4, insert relay buffer mid-net.
  4. The Chip Is Now Timing-Clean — slack red→green, power penalty small.
  5. The Impact — speedup vs Manual / Nexus / ChatEDA; "only system with live sandbox + self-correction on ASIC."

9. State Management

lib/store/useDesignStore.ts (Zustand) holds: design, initialDesign, iterations, timeline, staResult, fixResult, ppaResult, benchmarks, plus UI flags (loading, runningStage, judgeMode, selectedCornerName, isMockMode, closureAchieved). Helpers buildTimelineEntryFromAgent and buildApplyTimelineEntry convert agent outputs into timeline entries.


10. Sponsor Integrations

Sponsor Status Detail
OpenAI Stubbed getAgentClient() has a real-mode branch, but it currently returns the same mock agent functions (only flips isMock). Prompts in lib/agents/prompts.ts are ready for GPT-4o wiring.
Redis Partially real getRedisClient() uses real ioredis when REDIS_URL is set, else an in-memory MockRedis (full get/set/del/lpush/rpush/lrange/llen/hset/hget/hgetall/flushall). Actually used keys: design:current, design:iterations, audit:events.
W&B Weave No-op getWeaveClient() always returns MockWeaveClient; .trace() wraps every agent call and logs [MOCK WEAVE]. Real W&B is a TODO.
CopilotKit Simulated CopilotChat is a hand-rolled chat with canned responses + action buttons; not the real CopilotKit SDK.

All mock subsystems log with clear prefixes ([MOCK REDIS], [MOCK WEAVE], [MOCK AGENT]) so judges can open the console and see exactly what would be sent to real systems.


11. Implementation Status vs. The Pitch (honest gap analysis)

Pitch claim Status Notes
STA agent finds violation ✅ Implemented Rule + timing-engine based
Fix agent suggests ECO ✅ Implemented Heuristic (upsize / buffer insert / move)
PPA agent checks tradeoffs ✅ Implemented Accept/modify/reject verdicts
Orchestrator applies + recomputes + stops ✅ Implemented ≤5 iters, closure & diminishing-returns stops
Timing recomputed from real formulas ✅ Implemented timingEngine.ts cubic RC model
Red path → green animation, slack −0.21 → +0.05 ✅ Implemented ChipGraph + Judge Mode
MCMM / signoff / ECO legality (Cadence parity) ✅ Implemented MCMM panel + ecoBudget
Benchmark vs Nexus/ChatEDA/Manual ✅ Implemented nexusBenchmark.ts
OpenSTA report parsing ✅ Implemented reportParser.ts + endpoint
Redis "engineering memory" — store fix→result, rank, reuse later Not implemented Keys agent:sta:memory, agent:fix:memory, agent:ppa:memory, timing:reports, benchmark:results, timing:corners are defined but never read/written. No similarity search, ranking, or reuse logic exists.
Explainability agent surfaced in UI ⚠️ Partial Agent works; dashboard uses hardcoded narration instead of calling it
Real OpenAI / Weave / CopilotKit ⚠️ Stubbed Clean upgrade paths exist

11.1 Highest-leverage next step (the "strongest sponsor story")

The pitch's unique claim — engineering memory that accumulates experience — is the one thing not yet built. To deliver it:

  1. On each applied fix, write { rootCauseSignature, fix, slackBefore, slackAfter, design } to agent:fix:memory (Redis list or sorted set keyed by root-cause signature).
  2. In the Fix Agent, before recommending, query Redis for similar past root-cause signatures and rank candidate fixes by historical slackImprovement.
  3. Surface "Similar issue seen before — buffer insertion historically improved slack by +0.24ns" in the UI (a memory card / timeline note).

This converts the demo from "smart automation" into a genuinely agentic, learning system.


12. Constraints (honored)

  • ≤ 15 nodes in the demo graph (8 cells + buffers inserted by fixes).
  • No auth / no login.
  • ≤ 5 closure iterations (orchestrator hard-stops).
  • No real EDA engine — synthetic but physics-consistent timing.
  • 100% offline in mock mode for demo safety.

13. Research Baseline

  • Nexus (arXiv:2502.19091) — multi-agent timing closure on FPGA (VTR), 30% power saving.
  • ChatEDA (IEEE TCAD 2024, arXiv:2308.10204) — LLM agent for RTL-to-GDSII flow.
  • Manual baseline — Cadence Certus / TDF industry data (~180 min to close).
  • CloseLoop positioning: open-source, ASIC-targeted, self-correcting, live-visualized; closes the demo design in ~0.24–0.48 min (≈14–29s) vs. Nexus 3.2 min and Manual 180 min.

14. Glossary

  • STA — Static Timing Analysis.
  • ECO — Engineering Change Order (a targeted post-layout fix).
  • WNS / TNS — Worst / Total Negative Slack.
  • SlackrequiredTime − arrivalTime; negative = signal too late = violation.
  • PPA — Power, Performance, Area.
  • MCMM — Multi-Corner Multi-Mode analysis.
  • PVT — Process / Voltage / Temperature derating corner.
  • Drive strength — a cell's current-driving capability; higher = faster but more power/area.
  • Buffer insertion — adding a repeater mid-wire to break a long net into shorter segments.

Generated by reading the repository file-by-file, function-by-function (June 2026).