Skip to content

核心求解器概览

项目概览 | 中文说明

文档状态:持续维护的架构参考。 模块职责和数据流保持更新;文末性能与校准数字 属于带日期的快照,在新报告中引用前应重新运行对应基准。

本文介绍当前 circuitopt/ 求解器栈。代码是 AT4000TG OTFT ECG AFE 求解器的紧凑本地实现,已针对 Cadence/Spectre 行为进行校准。它是更广泛的本地电路优化流程的第一个具体后端。

覆盖范围

当前求解器栈覆盖:

  • DC 工作点求解。
  • AC 小信号增益与带宽分析。
  • 噪声分析,包括闪烁噪声和热噪声。
  • 瞬态响应仿真。
  • 周期稳态(PSS)shooting 求解。
  • PSS 辅助的 PAC(周期 AC),支持解析伴随谐波平衡(默认)、可选 time-domain Floquet shooting 加速路径,或有限差分 shooting。
  • 周期 PNoise,支持谐波平衡路径和 time-domain Floquet-adjoint 路径,并含循环平稳噪声折叠。
  • 工艺角与逐器件 mismatch 扰动。
  • 面向 Cadence/Spectre 的验证,涵盖工作点、AC、噪声、瞬态、PSS、PAC 和 PNoise 行为。

实现刻意保持小而自包含,包含 __init__.py、CLI 入口 __main__.py、校准/PSF/Cadence 网表辅助模块、共享诊断/profiling 模块、主求解器栈、一套 ML surrogate 层(数据集构建、surrogate 训练、筛选-校验优化器)、接入同一套 TransistorModel 接口的三个硅 PDK——SKY130(随包解析卡 + 原生 Berkeley BSIM4.5)、FreePDK45(平铺卡解析 + 原生 Berkeley BSIM4.5)与 TSMC28HPC+(内部 HSPICE 解析器 + 同一原生后端)—— 以及架在整个栈之上的可选本地 HTTP 服务层(circuitopt/service/)。

文件结构

circuitopt/
  topology.py          电路拓扑单一事实来源。
  compiled_topology.py 运行态拓扑/index/stamp 元数据编译层。
  circuit_loader.py    JSON 电路描述加载器。
  device_model.py      TransistorModel ABC + OtftParams + 模型工厂/注册表 + PDK/极性分层。
  device_factory.py    器件构建/解析层(build_devices、get_ss_params)+ corner 路由(OTFT CORNERS、
                        硅工艺 apply_silicon_corner)。leaf 模块:只依赖 device_model。
  pmos_tft_model.py    AT4000TG PMOS-OTFT 紧凑模型实现。
  ac_mna.py            MNA stamp 原语。
  ac_solver.py         纯 AC 小信号求解器:DC 工作点 + AC 响应(ac_solve)。
  dc_solver.py         DC 求解 fallback(有界最小二乘)+ AFE 专用对称 DC seeding/续流启发式。
  noise_solver.py      噪声传播与等价输入噪声分析。
  transient_solver.py  时域瞬态求解器。
  transient_profile.py 瞬态/chopper 求解器共享分析计数器槽位。
  pss_solver.py        基于 transient shooting 的 PSS 求解器。
  pac_solver.py        通用 PSS 辅助 PAC 求解器。
  pnoise_solver.py     通用 PNoise 求解器(HB + TD adjoint)。
  adaptive_config.py   自适应步进配置共享类型和工具函数。
  analysis_dispatch.py JSON 分析配置 dispatch 入口。
  analysis_options.py  面向 JSON dispatch 的中央求解器选项注册表。
  diagnostics.py       线程安全的求解器回退观察器(计数器 + 日志)。
  psf.py               Spectre PSFASCII 参考数据解析器。
  calibration.py       本地结果与 Cadence 参考的校准/比较工具。
  cadence_netlist.py   用于验证的 Spectre 网表生成工具。
  chopper.py           理想与 PMOS 开关差分 chopper 分析。
  explore.py           设计空间探索 / 优化驱动。
  corners.py           工艺角、mismatch MC 与 latch 检测。
  dataset.py           带 provenance、保留失败样本的 surrogate 训练数据集构建器。
  surrogate.py         基线指标 surrogate(GBT,可选 scikit-learn)+ 感兴趣区域过滤。
  surrogate_torch.py   可微 surrogate(torch/MPS)+ 基于梯度的设计优化。
  optimize.py          surrogate 筛选 / Pareto 选择 / solver 校验闭环。
  sky130_model.py      SKY130 兼容导出 + 显式 ngspice 参数卡提取工具。
  ngspice_char.py      model-card 求值器:批量 ngspice .dc/.noise 表征 → 缓存 (Vsb,Vds,Vgs) 网格。
  ngspice_device.py    基于缓存 ngspice 网格的 TransistorModel(插值 Id/gm/gds/caps/noise;extract_w + 温度)。
  ngspice_process.py   工艺适配协议:deck 前导、器件语法、op 向量、仿真器参数。
  freepdk45_model.py   FreePDK45 兼容导出 + 显式 ngspice oracle 注册。
  pdk/freepdk45/       平铺模型卡加载器与原生 FreePDK45 nmos/pmos 适配器。
  pdk/sky130/          随包解析卡加载器与原生 SKY130 nmos/pmos 适配器。
  compact_models/bsim4/ 原生 Berkeley BSIM4 host、ABI,以及连到编译 Rust core 的 ctypes 桥接层。
  tsmc28_model.py      TSMC28HPC+ core nmos/pmos:nch_mac/pch_mac + HSPICE library 闭包。
  service/             可选的本地 FastAPI HTTP 服务层(`serve` extra)——见下文。
    __init__.py        只重导出 CLI 胶水代码;从不 import fastapi(import circuitopt 保持无 fastapi 依赖)。
    app.py             create_app() —— /api/v1 路由(health/capabilities/validate/solve/jobs/*);薄适配,无数值逻辑。
    operations.py      HTTP 与 MCP 共用的能力查询/校验/求解 application operations。
    jobs.py            可扩展进程内线程池任务,带进度队列 + 协作式取消。
    serialize.py        to_jsonable()/serialize_results() —— numpy/complex/NaN → 严格 JSON 的转换约定。
    cli.py              add_cli_args()/run_cli() —— 共享的 `serve` 子命令参数定义(延迟导入 fastapi/uvicorn)。
  mcp/                 可选 stdio/Streamable HTTP MCP 适配层(`mcp` extra)。
    server.py          MCPServer 工具/resources、有界响应和后台 signoff 适配。
    workspace.py       工作区相对路径校验与原子结果写入。
    cli.py             `mcp` 子命令和独立入口共用参数。

导入关系

模块级导入构成一个十层的有向无环图。下图给出分层结构,其后是完整的边列表。

flowchart TB
  subgraph BASE["L0 — 无内部依赖"]
    direction LR
    B1["topology<br/>compiled_topology"]
    B2["device_model"]
    B3["run_contract · analysis_options<br/>adaptive_config · diagnostics"]
    B4["_rust_lti · _rust_transient<br/>_rust_periodic"]
  end

  subgraph DESC["L1–L2 — 描述与器件"]
    direction LR
    D1["circuit_loader"]
    D2["device_factory"]
    D3["pdk/* · compact_models/bsim4<br/>spice/*"]
  end

  subgraph SOLV["L2–L4 — 求解器"]
    direction LR
    S1["dc_solver"] --> S2["ac_solver"]
    S2 --> S3["noise_solver"]
    S2 --> S4["transient_solver"]
  end

  subgraph PER["L5–L6 — 周期稳态族"]
    direction LR
    P1["pss_solver"] --> P2["pac_solver"] --> P3["pnoise_solver"]
  end

  DISP["L7 — analysis_dispatch"]

  subgraph WF["L5–L7 — 工作流"]
    direction LR
    W1["explore · corners"]
    W2["dataset · optimize"]
    W3["sar · sar_mc · sar_explore"]
    W4["chopper · calibration"]
  end

  CAMP["L8 — signoff_campaign<br/>L9 — engine_crosscheck"]

  subgraph NG["ngspice 对照引擎"]
    direction LR
    N1["ngspice_render"] --> N2["ngspice_ac<br/>ngspice_transient"]
  end

  subgraph SVC["L9 — 服务层(纯消费叶)"]
    direction LR
    V1["service/*"]
    V2["mcp/*"]
  end

  BASE --> DESC
  DESC --> SOLV
  DESC --> NG
  SOLV --> PER
  SOLV --> DISP
  PER --> DISP
  SOLV --> WF
  PER --> WF
  DISP --> CAMP
  DISP --> SVC
  WF --> SVC
  CAMP --> SVC

这张图上有两条性质值得单独说明,因为二者都是承重的:

  • explorecornersdatasetchopper 直接调用求解器,不经过 analysis_dispatch。dispatch 负责让一个电路跑完一套配置好的分析;这四个模块 驱动的是候选矩阵,需要直接拿到求解器。
  • service/mcp/ 是纯消费叶子。 二者之外没有任何模块反向导入它们,且 circuitopt/__init__.py 从不导入其中任何一个,因此即使装了 serve extra, 普通的 import circuitopt 也不会引入 fastapi。

下面的完整列表只记录模块级导入。有两对模块——run_contract/dc_measurementssar/sar_rust——还会在函数内部互相导入;这是 Python 中打破导入环的惯用做法, 也是模块级图能保持无环的原因。

topology.py            <- 无内部依赖
compiled_topology.py   <- 无内部依赖; 运行时消费 Topology 形状的对象
device_model.py        <- 无内部依赖 (仅 abc / dataclasses)
ac_mna.py              <- 无内部依赖
run_contract.py        <- 无内部依赖 (dc_measurements 为延迟导入)
analysis_options.py    <- 无内部依赖 (注册表)
adaptive_config.py     <- 无内部依赖 (仅 dataclass)
transient_profile.py   <- 无内部依赖 (计数器槽位常量)
diagnostics.py         <- 无内部依赖 (线程安全计数器)
psf.py                 <- 无内部依赖
adc.py                 <- 无内部依赖
frequency_metrics.py   <- 无内部依赖
passive_bom.py         <- 无内部依赖
toolchain.py           <- 无内部依赖
surrogate.py           <- 无内部依赖; 运行时可选 scikit-learn/joblib
_engine.py             <- 无内部依赖
_parallel.py           <- 无内部依赖
_rust_lti.py           <- 无内部依赖 (序列化桥)
_rust_transient.py     <- 无内部依赖 (序列化桥)
_rust_periodic.py      <- 无内部依赖 (序列化桥)
spice/parser.py        <- 无内部依赖
spice/expressions.py   <- 无内部依赖
spice/elaborator.py    <- spice/parser, spice/expressions
circuit_loader.py      <- topology, device_model
device_factory.py      <- device_model, run_contract (叶子器件层;不导入求解器/工作流)
dc_measurements.py     <- run_contract
pmos_tft_model.py      <- device_model (方程在 circuitopt_core.OtftModel 中)
cadence_netlist.py     <- topology
_campaign_sweep.py     <- _engine
compact_models/bsim4/abi.py         <- 无内部依赖
compact_models/bsim4/card_cache.py  <- 无内部依赖
compact_models/bsim4/native.py      <- compact_models/bsim4/abi
compact_models/bsim4/rust_transient.py <- _rust_transient, compiled_topology, bsim4/native
compact_models/bsim4/transient.py   <- adaptive_config, compiled_topology, device_factory
pdk/<name>/library.py  <- compact_models/bsim4, spice, toolchain
pdk/<name>/device.py   <- compact_models/bsim4, device_model, pdk/<name>/library
sky130_model.py        <- pdk/sky130, toolchain
freepdk45_model.py     <- pdk/freepdk45, device_model, ngspice_device
tsmc28_model.py        <- device_model, ngspice_device, ngspice_process, toolchain
ngspice_char.py        <- toolchain; 仅 ngspice 子进程 + numpy
ngspice_process.py     <- device_model
ngspice_device.py      <- device_model, ngspice_char; 运行时可选 scipy
ngspice_render.py      <- device_factory, freepdk45_model, ngspice_process, toolchain
ngspice_ac.py          <- device_factory, ngspice_char, ngspice_render
ngspice_transient.py   <- device_factory, ngspice_char, ngspice_render
dc_solver.py           <- device_factory, topology
ac_solver.py           <- compiled_topology, dc_solver, device_factory, run_contract, topology
noise_solver.py        <- ac_solver, compiled_topology, device_factory, device_model, run_contract, topology
transient_solver.py    <- ac_solver, adaptive_config, compiled_topology, device_factory, topology, transient_profile
pss_solver.py          <- ac_mna, ac_solver, adaptive_config, device_factory, topology, transient_solver
pac_solver.py          <- ac_mna, ac_solver, device_factory, topology, transient_solver
pnoise_solver.py       <- ac_mna, device_factory, noise_solver, pac_solver, run_contract
analysis_dispatch.py   <- ac_solver, noise_solver, transient_solver, pss_solver, pac_solver,
                          pnoise_solver, circuit_loader, analysis_options, adaptive_config, run_contract
calibration.py         <- ac_solver, adaptive_config, noise_solver
chopper.py             <- ac_solver, dc_solver, device_factory, device_model, adaptive_config,
                          noise_solver, pss_solver, pac_solver, pnoise_solver, topology, transient_solver
explore.py             <- ac_solver, circuit_loader, device_factory, frequency_metrics, noise_solver, run_contract
corners.py             <- _campaign_sweep, _parallel, ac_solver, circuit_loader, device_factory,
                          noise_solver, run_contract, topology
dataset.py             <- circuit_loader, device_factory, device_model, explore, run_contract, transient_solver
optimize.py            <- circuit_loader, dataset, device_factory, explore
surrogate_torch.py     <- surrogate (dataset 延迟导入,仅 CLI);运行时可选 torch
_rust_campaign.py      <- compiled_topology, device_factory, topology
sar.py                 <- _parallel, adc, circuit_loader, transient_solver
sar_mc.py              <- _parallel, adc, circuit_loader, sar
sar_rust.py            <- _rust_transient, compiled_topology, device_factory, sar, transient_solver
sar_explore.py         <- adc, circuit_loader, explore, sar, sar_mc
signoff_campaign.py    <- _parallel, analysis_dispatch, circuit_loader, compact_models/bsim4, run_contract
engine_crosscheck.py   <- analysis_dispatch, circuit_loader, compact_models/bsim4, device_model, signoff_campaign
service/serialize.py   <- 无内部依赖; 仅 numpy
service/jobs.py        <- corners, explore, service/serialize; 无 fastapi(纯 threading/queue)
service/operations.py  <- analysis_dispatch, analysis_options, circuit_loader, device_factory,
                          device_model, freepdk45_model, run_contract, service/jobs, service/serialize
service/app.py         <- service/operations, service/jobs; 导入时可选 fastapi/pydantic
service/cli.py         <- service/app (延迟);运行时可选 uvicorn
mcp/workspace.py       <- 无内部依赖
mcp/server.py          <- mcp/workspace, service/operations, service/jobs, service/serialize,
                          signoff_campaign; 导入时可选 mcp SDK
mcp/cli.py             <- mcp/server (延迟)

主要组件

pmos_tft_model.py

实现了 AT4000TG PMOS-OTFT 紧凑模型的 Python 版本。提供:

  • 通过 get_Idc 计算端电流。
  • 通过 get_noise_psd 计算漏极电流噪声 PSD。
  • 通过 get_capacitances 计算偏置相关的端电容。
  • 通过 g_area 计算几何面积。
  • 工艺和 mismatch 参数,如 pvt0mvt0pbeta0mbeta0
  • 带热启动的内部节点工作点求解。
  • 标量内核为编译执行:器件方程在 circuitopt_core.OtftModel 中求值 (v2.0.0 起为唯一引擎)。

AC 和噪声分析时,求解器通过有限差分 get_Idc 提取端 gmgds,与电路求解器使用的端行为保持一致。

PMOS_TFT 继承 :class:~device_model.TransistorModel,即所有求解器消费的抽象基类。 它还提供 get_otft_params() 供瞬态求解器编译内循环使用, 以及由编译 Rust core 解析端导数支撑的 get_ss_params() 覆盖方法。

device_model.py

定义抽象器件模型接口,将求解器与具体晶体管实现解耦:

  • TransistorModel (ABC) — 七个抽象方法(get_Idcget_opget_capacitancesget_capacitance_charges_from_opget_capacitance_branch_terms_from_opget_noise_psdget_otft_params);get_ss_params 提供有限差分默认实现,子类可覆盖。
  • OtftParams (frozen dataclass) — 16 个标量参数,瞬态求解器每个器件提取一次,传入编译内核。
  • 后端能力类属性 — 通用求解器按能力分派,而非具体后端类型。 HAS_TERMINAL_LINEARIZATION(默认 False)标记该模型是否暴露 AC/PAC/PNoise 使用的完整 quasi-static 4×4 terminal (G, C) stamp;原生 BSIM 器件设为 TrueTRANSIENT_BACKEND(默认 None,即走所选引擎的通用 OTFT 瞬态路径)指定专用积分器, 例如 "bsim4_native" 或显式外部 oracle 后端。
  • register_model() / create_device() + PDK/极性分层 — 工厂与注册表,见下文。

注册键。 每个 (pdk, polarity) 以结构化键 "<pdk>.<polarity>" 注册,例如 "at4000tg.pmos"register_pdk() 把一个工艺的各极性归组并标记默认。"pmos_tft" 保留为向后兼容别名。

单一切换点。 求解器调用 create_device(get_default_model_type(), …) 而非硬编码 模型名,因此新增工艺或 nmos 极性只需一次 register_pdk,不改任何求解器。

读取接口。 get_model_class(model_type) 是公开只读访问器,让求解器无需 import 具体后端类即可读取模型的能力标志。registered_models() 返回整个注册表的只读快照 {model_type: "module.QualName"}(按插入顺序),供需要枚举而非查单条的调用者使用; 服务层 GET /api/v1/capabilities 用它列出全部可选模型键。

不在注册表内。 通用元件——电阻、电容、理想电压/电流源、受控源——是与工艺无关的 拓扑原语,进入此注册表,因此每个 PDK 零改动复用。

名字冲突。 register_model() 重复注册时会覆盖旧条目,使有意的替换(例如测试打桩) 继续静默生效。真正的冲突——不同的类(按 __module__.__qualname__ 判断)抢占已被占用 的名字,例如两个 PDK 模块争同一别名——会在覆盖前发出 RuntimeWarning。对同一个类的 重复 import 或 importlib.reload 仍保持静默。

device_factory.py

leaf 器件构建层:只依赖 device_model(不 import 任何 solver 或 workflow 模块),因此所有求解器都 能放心 import 它而不引入循环依赖。

  • build_devices(sizes, *, nf=None, corner=None, topo, model_types=None, device_kwargs=None) / get_ss_params(...) — 把求解器已有的逐器件输入(sizes、NF、corner、model_typesdevice_kwargs)转换成具体的 TransistorModel 实例;原样从 ac_solver.py 迁入。 dev_corner/dev_nf/is_per_device_corner 是背后的逐器件 corner/NF 解析辅助函数。
  • CORNERS — OTFT 连续 PVT 全局工艺偏移 dict(typical/slow/fast,按 pvt0/pbeta0), 从 corners.py 迁入此处;corners.py 现在从这里 import 它,而不是自己定义。
  • SKY130_CORNERS / SILICON_CORNERS / apply_silicon_corner(model_types, device_kwargs, corner) — 硅离散 corner 路由,把一个 corner 名(SKY130 的 tt/ss/ff/sf/fs,加上 FreePDK45 的 nom)stamp 到解析好的硅器件卡上;从 explore.py 迁入此处;explore.pydataset.py 现在从这里 import 它,而不是自己定义。
  • CircuitBinding — frozen dataclass,把每个求解器过去逐个手工穿透的六个逐电路输入打包成一体: topo / model_types / device_kwargs / nf / corner / dc_seed。它的存在是为了钉死一类 bug: 向求解器传参时漏掉 model_types/device_kwargs,电路会静默退回默认 OTFT PDK。现在调用方传一次 binding= 即可,不再重新铺设这一整簇参数。用 CircuitSpec.binding() 构造(见 circuit_loader.py)。 binding.build(sizes) 为这些 sizes 生成 {name: TransistorModel}binding.at_corner(corner) 返回 一个路由到该 corner 的 binding——硅 corner 经 apply_silicon_corner 烘焙进 device_kwargs(并清空 solver corner),OTFT corner 留在 binding.corner 上,None 原样返回 self解析优先级(经 resolve_binding):显式传给求解器的非 None keyword 永远优先;否则由 binding 字段提供默认 (binding.dc_seed 兜底 x0_guess);binding=None 时与旧 kwargs 路径逐字节一致。六个求解器入口 (ac_solve/noise_analysis/transient/pss_solve/pac_solve/pnoise_solve)都接受 binding=, 内部工作流——analysis_dispatch.run_analysis_suiteexploredatasetoptimize——统一穿一个 binding,而不是逐分支重新转发 model 簇。

topology.py

将电路拓扑定义为单一事实来源。拓扑包含晶体管列表、被求解节点列表、rail/bias 节点、输出、AC 输入驱动、负载电容、瞬态输入映射、DC 初值猜测和 DC 别名。求解器运行态元数据均从这个拓扑派生,而不是在各个求解器中分别手写。

除了晶体管之外,还承载无源与源元件。这些元件都不影响晶体管模型相关逻辑。

元件 字段 语义
resistors a、b、R 阻值,单位欧姆。
capacitors a、b、C 容值,单位法拉。
isources nplus、nminus、I 理想直流电流源,I 从 nplus 流向 nminus。
vsources p、q、value 理想电压源,真 MNA。
vccs p、q、ctrl_p、ctrl_n、gm 压控电流源。
vcvs p、q、cp、cn、mu 压控电压源,Vp−Vq = μ(Vcp−Vcn)
cccs p、q、ctrl_name、beta 流控电流源,Iout = β·Ictrl
ccvs p、q、ctrl_name、gamma 流控电压源,Vp−Vq = γ·Ictrl

每个 vsource、VCVS 与 CCVS 新增一个支路电流未知量和一行约束,系统从 n 增长到 n_aug = n + m。CCCS 与 CCVS 支持级联:二者都可以控制在任意 vsource、VCVS 或 CCVS 的支路电流上。

这些元件贯穿全部分析:

  • DC —— 电阻支路电流与电流源注入进入 KCL。
  • AC —— 电阻按 1/R stamp,电容按 jωC,VCCS 按 gm*(Vcp-Vcn), VCVS/CCVS/vsource 按 bordered [[Y,B],[B^T,0]] 块连同各自约束行 stamp, CCCS 按 KCL 行耦合。电流源在小信号系统中视为开路。
  • 噪声 —— 电阻贡献 4kT/R 热噪声。所有受控源与理想电压源无噪声。
  • 瞬态 —— 电导、电容伴随模型、恒定/VCCS/CCCS 源电流,以及带约束方程的 VCVS/CCVS/vsource 支路电流未知量。

默认拓扑是 AFE_TOPO,一个 10 管全差分 AFE 核心,包含尾电流器件、输入对、输出级和交叉耦合正反馈电平移位器件。

compiled_topology.py

从声明式 Topology 以及当前 bias/input 上下文构建运行态 plan。它会把节点名一次性解析成紧凑 terminal token,并为 DC、AC/噪声和 transient 暴露共享元数据:

  • solved-node index 和 rail 数值;
  • 每个器件的 drain/gate/source terminal token;
  • 电阻、电容、电流源、VCCS、VCVS、CCCS 和 CCVS 的 stamp 元数据;
  • AC/噪声使用的 ("n", idx) / ("v", value) 端表;
  • transient input 与 node_inputs 映射。

这样 AC、noise 和 transient 使用同一套 indexing/stamping 约定,同时仍能保持 JSON 电路替换能力。

它还承载两个小的编组辅助函数 term_arrays()(把 (kind, ref_or_value) terminal token 拆成并行的 kind/ref/value int/float 数组)和 index_array()(把可选整数 index 打包成 int64 数组,None-1)。通用瞬态与原生 BSIM 瞬态编组都 基于这两个辅助函数构建同一套 stamp-ready 数组,所以它们和拓扑 token 放在一起,而不是每个后端各自 复制一份。

circuit_loader.py

加载 JSON 电路描述并返回 CircuitSpec,包含:

  • topology
  • sizes
  • bias
  • nf

这使得可以通过 JSON 文件(如 examples/single_stage.json)添加新电路,而无需修改求解器源码。CircuitSpec.binding() 把 spec 的 topologymodel_typesdevice_kwargsnf 以及默认 DC 初值(其第一个 dict 型 dc_guess)打包成一个 CircuitBinding(见 device_factory.py),这样工作流可以向求解器传 binding=,而不必逐个穿透这一整簇参数。

numba_kernels.py(已于 v2.0.0/R7 移除)

历史模块:曾承载纯 Python/numba 的 _impl 标量与瞬态内核。R6 移除 numba JIT; R7 在把其承重部分——OTFT 选根恢复 oracle——逐位移植进编译核 (circuitopt_core.OtftModel(..., reference=True),由 pmos_tft_model.otft_reference_mode 选择)之后整文件删除。冻结 golden 语料 (tests/golden/engine_parity)是数值参考 oracle。

analysis_options.py / analysis_dispatch.py

analysis_options.py 是中央的逐分析选项注册表,analysis_dispatch.pyrun_analysis_suite(以及 JSON schema 回归测试)都从它派生,故 solver kwargs/ 默认值/schema 不会互相静默漂移。validate_analysis_cfg(analysis, cfg) 拒绝 JSON analyses 块里的残留键:known_keys(analysis) 把该 solver 的选项注册表 和 DISPATCH_KEYSrun_analysis_suite 直接从 cfg 里读、而不转发进 solver_kwargs 的少数键——如 ac/noisefreqs/corner/bandtransientsigned_devices)取并集;任何键落在这个并集之外都会抛 ValueError,点名该分析名、出错的键、以及排序后的合法键列表。这把一次拼写 错误(比如把 max_sideband 拼成 max_sidebands)从"静默用默认值跑掉"变成 立即报错。

analysis_dispatch.py 里的 ANALYSIS_ORDER = ("ac", "noise", "transient", "pss", "pac", "pnoise") 是权威的分析名元组与执行顺序;run_analysis_suite 遍历它, 服务层 GET /api/v1/capabilitiesanalyses 映射也遍历同一个元组来构建, 两处列出的分析名因此天然一致,不需要另外维护一份硬编码列表。

psf.py

provenance(path)["fundamental"] 读取 PSF HEADER 里 "fundamental frequency" 键(若某个非标准写入器只给出裸 "fundamental" 拼法则回退到它)—— 周期性分析(PAC/PNoise/PSS)会报出真实驱动频率;DC/AC/noise/tran 两个键都没有, 读回 Noneparse_noise(path) 的逐器件噪声数组是ragged 的:列数跟随每个 器件在 TYPE 段声明的 struct 宽度(MOSFET 的 (flicker, thermal, total) struct 宽度为 3;电阻的 (rn, total) struct 宽度为 2),故调用者要取最后一列[:, -1])拿总量,且要先查 .shape[1] 再去切某个具体字段——不能假设宽度 总是 3。

ac_mna.py

提供小信号求解器使用的底层 MNA stamp 原语:

  • 导纳 stamp。
  • VCCS、VCVS、CCCS、CCVS stamp。
  • 理想电压源 stamp(bordered MNA)。
  • MOS 小信号 stamp。

ac_solver.py

求解 DC 工作点和 AC 响应:

  • ac_solve(sizes, bias, freqs, corner=None, x0_guess=None, topo=AFE_TOPO, nf=None)
  • 使用 scipy.fsolve 求解 DC 节点方程。
  • 返回增益、带宽、节点工作点以及提取的小信号参数。
  • 同时支持全局工艺角和逐器件 mismatch 映射。
  • 使用拓扑元数据确定输出、负载电容和 AC 输入驱动。

DC 求解包含物理支路选择、对称工作点和 rail 有界节点解的鲁棒性处理——实现在 dc_solver.py 里, 由这里调用。ac_solver.py 本身现在是纯 AC 小信号模块:ac_solve 加上带宽辅助函数 bw_from_gain;不再承载器件工厂或 DC seeding 代码。

dc_solver.py

DC 工作点求解支持代码——以前内嵌在 ac_solver.py 里,现在拆出来,因为它混杂着两类不同的关注点:

  • bounded_least_squares_dc(...) / dc_residual_ok(...) — 通用的最后手段 DC 求解:当主 Newton/fsolve 路径不收敛时使用的有界最小二乘 fallback,以及门控它的残差判定。
  • symmetric_seed(...) / symmetric_continuation(...) / is_afe_topology(...) / is_pairwise_symmetric_afe(...) / _AFE_SYMMETRIC_PAIRS — 只针对 AFE 拓扑的电路专用 seeding 启发式,不是通用求解器逻辑。它为这一个电路选出物理上(匹配 Spectre)的对称上电支路。 单独放一个模块能让 ac_solver.py 的通用求解保持不含逐电路分支。

noise_solver.py

在与 AC 分析相同的拓扑派生 MNA 系统上执行噪声传播。每个晶体管漏极电流噪声源注入到漏源之间,传播到配置的输出,并除以信号增益得到等价输入噪声。

噪声流程支持与 AC 求解器相同的拓扑派生端映射和 corner/mismatch 参数传递。 当 JSON dispatch 中 AC 与 noise 使用同一频点网格时,noise 会直接复用前面 AC 的 dc_op、小信号参数和增益,避免重复 DC/AC 求解;频点不同则至少复用 dc_op 作为 warm seed。

chopper.py

计算 AFE 周围不同 chopper 版本的 gain、带宽和基带噪声:

  • chopper_analysis(...) 是理想同步差分 chopper 模型,把八开关换向器看作输入 和输出端的 +/-1 方波乘法器,再用奇次谐波系数把边带 gain/noise 折回基带。 这是描述理想 chopping 与 flicker noise 搬移的 LPTV 频域路径。
  • build_afe_pmos_chopper(...) 会在 AFE 输入/输出端口周围插入 8 个真实 PMOS_TFT pass switch。
  • pmos_chopper_analysis(...) 对这个 PMOS 开关拓扑分别运行静态 A/B 相 AC 和 noise,并对两相平均;结果包含 switch Ron 负载、非线性电容和 PMOS switch 自身噪声。
  • finite_edge_clock_pair(...)finite_edge_chopper_harmonics(...) 建模有限 clock edge 和 break-before-make dead time 对 chopper 谱线权重的影响。
  • pmos_chopper_lptv_analysis(...) 用这些有限边沿谐波权重折叠 PMOS-switch sideband response/noise。这是一个快速的一阶 quasi-static 估计,会低估基带 增益约 10%(漏掉了高阶 LPTV 变换)。要 Cadence 级的增益/噪声请用无经验常数的 谐波平衡路径(pmos_chopper_psspmos_chopper_pac/pmos_chopper_pnoise)。 原来那两个 Cadence 拟合常数(换向相位/噪声 PSD scale)已retire; conversion_phase_rad/periodic_noise_psd_scale 仍保留为手动可调参数。
  • pmos_chopper_transient(...) 用有限边沿 clock 驱动八 PMOS 拓扑。默认 clock 采用 Spectre type=pulse 语义(delay=T/2width=T/2、有限 rise/fall); 旧的居中相位波形仍可通过 clock_style="phase" 使用,适合 dead-time 实验。 clock feedthrough 来自 PDK Cgss/Cgdd * ddt() 项以及 PDK Verilog-A 中长期有效的 R_cap2 gate-leak 分支,均由 transient solver stamp;可选 charge injection 脉冲由同一套 PDK 电容公式估算,并作为时变电流源注入。这个 helper 会在 clock 边沿附近自动加密内部时间网格,对 8 个双向 pass switch 使用 signed terminal current,并收紧残差容差,避免慢 common-mode 电荷平衡被忽略。
  • pmos_chopper_pss(...) 把同一 hard-switched 八 PMOS 拓扑接入通用 shooting PSS 求解器,返回给定 clock 周期上的周期稳态轨道。这是后续原生 PAC/PNoise 的本地工作点基础。该 wrapper 默认使用 cap_mode="average" 生成轨道,即 0.5*(C_n+C_{n-1})*dV 的梯形电容离散,用来匹配 Cadence 在 chopper 高阻内部节点上的 commutation feedthrough;通用 transient/PSS 仍默认使用电荷守恒 Q-stamp。
  • pmos_chopper_pac(...) 是 chopper 兼容包装器,内部调用通用 circuitopt.pac_solver.pac_solve(...)。通用 solver 仍默认使用解析伴随 HB;chopper wrapper 默认使用 time-domain Floquet PAC:在时域构建一次单周期 monodromy, 然后每频点求一个小的 quasi-periodic 边界系统(method="pss_time_domain")。 对 PMOS_TFT 周期转换,它会保留每个器件的内部 gate1 小信号状态(R_capR_cap2CgsCgd),不再逐时刻塌缩成端口 {gm,gds,Cgs,Cgd}。 周期转换线性化使用 Spectre PAC 折叠的 Verilog-A 风格 C(V)*ddt(V) 算子, 不必等同于生成大信号 PSS 轨道时的 transient companion。所有 PMOS 都暴露 gate1 网络时,gate1 扩维 PAC 线性化由编译 Rust 内核 (circuitopt_core.PeriodicLinearizationProblem)装配;它尚不覆盖的拓扑 (受控源、bordered 或 vsource 驱动系统)会提前返回,由调用方继续尝试下一条 路径。设置 time_domain=False 可跑解析伴随 HB 对照路径;设置 analytic=False 可回退到原有限差分 shooting 路径。静态 PSS 会自动退化为普通 ac_solve fast path,不跑 PAC transient。
  • pmos_chopper_pnoise(...) 是 chopper 兼容包装器,内部调用通用 circuitopt.pnoise_solver.pnoise_solve(...)。chopper 验证默认使用 time-domain Floquet adjoint:直接求稀疏周期伴随 BVP,再复用现有循环平稳器件/电阻噪声折叠。 这去掉了 HB adjoint 边带截断误差。谐波平衡 PNoise 路径仍可用 time_domain=False 显式调用:沿轨道 N 点采样 → 时变小信号 G(t)/C(t) → FFT 到频域 → 组装 nb×nb 块矩阵 Y[kr,kc] = G_{kr-kc} + jω·C_{kr-kc} → 每基带频率一次伴随求解得到传递阻抗 Z_{j,k} → 噪声折叠到 baseband 输出。 与 pmos_chopper_lptv_analysis 不同,两条路径都无需 Cadence 标定常数。

PMOS-switch sideband 路径最初使用 pmos_chopper_lptv_analysis 配合 Cadence 标定 常数验证通过。原生 pmos_chopper_pacpmos_chopper_pnoise 现在已替代这些依赖 标定的路径,提供第一性原理的周期小信号和噪声求解。瞬态 finite-edge 路径已与 Spectre tran 对齐。对 D3 / chop_tb_d3 官方 slow corner PSS/PAC/PNoise 参考,默认 time-domain PAC 约 +0.03%,原生 TD PNoise IRN 约 +0.02%。旧 HB-K32 PNoise IRN 三 corner 误差为 slow/typical/fast = +1.81% / +1.05% / +0.66%; TD adjoint 后为 +0.02% / −0.00% / +0.57%。这把此前由边带截断造成的“假舒适” 彻底揭掉,同时仍是第一性原理求解。

pss_solver.py

在现有 transient 引擎之上做 shooting PSS:

  • pss_solve(sizes, bias, period, topo=..., tgrid=..., inputs=..., node_inputs=...)
  • transient(...) 积分一个周期,并求解 x(T)-x(0)=0
  • 默认用 DC 工作点作为初值;可先跑若干 stabilization 周期再进入 shooting。
  • Shooting Jacobian:默认(analytic_jacobian=True)直接在收敛轨迹上一次性 遍历构建:采样每步的小信号 G(t)/C(t) stamp,按后向欧拉离散化形成 A_m = (G_m + C_m/h)^{-1} · (C_m/h),累积 monodromy 矩阵 Φ = ∏ A_m, 得到 Jacobian = Φ - I — O(1) 遍历替代 n_state 次有限差分瞬态。失败时 自动回退到有限差分。设置 analytic_jacobian=False 可强制使用原有限差分路径。
  • 首轮 Jacobian 构建后(无论解析还是 FD),默认用 Broyden secant update 复用 Jacobian,减少多轮 shooting 时的重复构建。每一步仍用真实一周期 transient 重新计算 residual,精度判据不变;可用 jacobian_reuse=False 恢复每轮重建, 或设置 jacobian_rebuild_interval 周期性重建。
  • 返回一周期轨迹、x0x_end、残差向量/范数、收敛标志和迭代历史。 结果中还包含 shooting_period_runsshooting_jacobian_evalsshooting_jacobian_reuses 等性能计数器。历史记录中可查看所用的 Jacobian 类型("analytic_monodromy""finite_difference")。
  • PMOS chopper wrapper 默认使用编译 Rust grid transient 路径 (fallback_least_squares=False),并在为 PAC/PNoise 内部构造 PSS 轨道时 使用 1 个 stabilization 周期和 chopper 专用 cap_mode="average" 轨道。这样 residual/nfail 收敛判据不变,同时避免每个周期退回 Python fallback 重跑。
  • chopper PSS 自动初值会缓存同一 bare AFE 尺寸/bias/corner 的 DC seed;重复分析 同一候选时只复用初始猜测,仍会重新跑真实 shooting residual,因此不改变精度判据。

这一步输出的 PSS 周期轨道可以直接供通用 pac_solvepnoise_solve 使用; pmos_chopper_pac / pmos_chopper_pnoise 只是把 chopper 的差分输入 drive 映射成通用求解器需要的 input_drive={"vip": 0.5, "vin": -0.5}

pac_solver.py

  • pac_solve(sizes, bias, freqs, pss_result=..., input_drive=...)
  • 与具体电路无关,只要求 PSS 结果包含 topologytnodesx0x_endoutput 以及周期输入波形元数据。
  • input_drive 是小信号复幅值映射,例如差分输入为 {"vip": 0.5, "vin": -0.5}, 单端输入为 {"vin": 1.0}
  • 四条性能路径,按优先级依次尝试:
  • LTI fast path — 静态 PSS 轨道直接退化为普通 ac_solve
  • Time-domain Floquet PACtime_domain=True;chopper wrapper 默认)— 在均匀轨道网格上 采样周期性 G(t)/C(t) 和输入耦合,先构建一次与频率无关的 monodromy, 再对每个频点求 (exp(jωT)I - Ψ)x0 = g。它避免 HB 边带截断和大型 (2K+1)n 转换矩阵。PMOS_TFT 器件会在周期转换中扩展内部 gate1 小信号状态;全 PMOS gate1 拓扑走编译 Rust 装配内核。不支持 mixed/bordered/vsource 驱动时返回 None 并继续尝试下一条路径。
  • 解析伴随(通用默认,analytic=True)— 沿 PSS 轨道采样周期性 G(t)/C(t) 和输入耦合列 G_in(t)/C_in(t),FFT 到谐波系数,构建谐波平衡转换矩阵 Y_HB(f),每频率一次伴随线性求解得到 sideband-0 增益。O(1) 求解,零额外 瞬态运行。由 n_period_samples(时域分辨率)和 max_sideband(边带数)控制。
  • 有限差分 shootinganalytic=False)— 有限差分状态转移矩阵 Φ 和 复输入扰动,求解 (Φ-γI)dx0=-b。每频点需 n_state+2 次瞬态运行。 结果缓存在 pss_result 上供重复调用。
  • 结果中包含 pac_period_runspac_state_cache_hitpac_input_cache_hitspac_td_setup_time_s 等计数器,以及 method 字段 ("pss_time_domain""pss_analytic_adjoint""pss_fd_shooting")。 PAC condition 诊断默认关闭,只会在 profile=Truedebug=True 或显式 compute_condition=True 时启用;该诊断每频点做一次 SVD,不影响 gain/BW/noise。

pnoise_solver.py

  • pnoise_solve(sizes, bias, freqs, pss_result=..., fundamental=...)
  • 基于通用 Topology 的器件、电阻、电容 stamp;PMOS 器件噪声沿 PSS 轨道采样, 电阻热噪声按 stationary source 折叠。
  • 静态 PSS 轨道会直接走普通 noise_analysis 的 LTI fast path;真正的 LPTV 路径会把采样 G(t)/C(t)、HB block 和相同频点的 adjoint 解缓存到 pss_result(HB 路径适用时)。
  • 设置 time_domain=True 时,PNoise 会用稀疏 Floquet 伴随 BVP 替代 K 截断的 HB adjoint 求解(结果字段 pnoise_time_domain_used=True)。这条路径在转换边带上无截断; 剩余误差主要来自时域网格离散,因此默认式 n_period_samples < 640 会自动抬到 768。 这条路径返回 method="pss_time_domain_floquet_adjoint";HB 兜底路径返回 method="pss_harmonic_balance_conversion_matrix"
  • HB adjoint 求解支持 hb_solver="auto" | "dense" | "sparse" | "iterative"。 默认小矩阵继续走 dense BLAS/LAPACK;HB 规模变大且非常稀疏时切到 SciPy sparse direct。强制 iterative 时使用按谐波对角块 LU 的 block-Jacobi 预条件 GMRES, 若不收敛会回退 sparse direct,避免损失精度。
  • HB block 组装、标量 freq × source × sideband² 噪声折叠、OTFT 轨道 线性化、保留的 gate1 动态,以及硅器件通用四端口 G/C 盖章都由编译 Rust core(circuitopt_core)执行——v2.0.0 起的唯一引擎(见 circuitopt/_engine.py)。 结果字段 pnoise_rust_hb_used / pnoise_rust_fold_used 标记某次调用是否 命中了 Rust 快速路径。编译实现主要能加速矩阵填充和 噪声折叠循环;HB 线性求解本身主要由 BLAS/LAPACK、SuperLU 或 GMRES 决定, 不是 Python 循环开销主导。
  • 如果未显式传入 gainspac_result,可传入同一套 input_drive,函数会调用 通用 pac_solve 得到输入参考所需的 PAC 增益。

transient_solver.py

使用后向欧拉(默认)或变步长 BDF2/gear2 积分求解拓扑定义系统的时域响应:

  • transient(sizes, bias, tgrid, vip=None, vin=None, nf=None, V0=None, topo=AFE_TOPO, inputs=None, node_inputs=None, integration_method="be", adaptive=False)
  • 支持传统的 AFE vip/vin 输入,也支持通过 topo.transient_inputs 驱动的通用 inputs={name: waveform}
  • node_inputs={node: input_key} 在某个(rail)节点上驱动波形——用于前端 testbench,其激励在源节点注入并通过无源网络传播,而非直接驱动器件栅极。
  • current_inputs=[{"p": node_a, "q": node_b, "input": key}] stamp 一个时变 理想电流源,方向为 p -> q;PMOS chopper helper 用它注入 charge-injection 脉冲。
  • max_stepmax_retry_subdivisionsfallback_full_jacobianfallback_least_squares 用于 switched transient 步的受控细分和有界 fallback 求解。
  • cap_mode / cap_mode_id 是 per-call 电容算子 override;生产路径只支持 charge/id 0 和 average/id 1。None 使用环境默认 charge,chopper PSS 显式传 average 匹配 Cadence feedthrough。该 override 只影响 transient/PSS 轨道,不影响 PAC/PNoise conversion 线性化。
  • adaptive=True 是 opt-in 的 LTE-controlled gear2 路径:传入的 tgrid 作为输入采样网格 和 [t0, tstop] 边界,返回自选的非均匀 accepted grid。它只允许配合 integration_method="gear2"。公开 API 仍兼容 adaptive_reltoladaptive_vabstoladaptive_iabstoladaptive_max_stepsadaptive_h0;内部会统一归一化成 AdaptiveConfig,供 transient/PSS/chopper 共用。PSS 额外从同一个 config 读取 adaptive_freeze_factor。 adaptive LTE policy 的常量和 Python helper 位于 circuitopt/adaptive_config.py; 编译 Rust core(co-core)保留一份对应 mirror,并由测试校验两边一致。Newton 失败的 reject 现在会缩小候选步长,零误差 accepted step 才允许放大。
  • 包含拓扑定义的负载电容(及电容元件),加上电阻和理想电流源支路。
  • 在牛顿迭代期间重新计算非线性电容,并包含 PDK Verilog-A 使用的 PMOS R_cap2 源/漏到 gate 的泄漏支路。
  • 支持 signed_devices,用于双向 pass switch。默认 AFE 路径保持与已校准 DC/AC/noise 求解器一致的 abs(Idc) 约定;开关器件则可在源漏电压反向时保留 物理 drain-current 符号。
  • 使用来自 ac_solve 的 DC 工作点作为默认初始条件。
  • 使用编译 Rust transient Newton kernel(circuitopt_core,通过 _rust_transient.build_otft_transient_problem 构建)。该路径在一个内循环里 完成 PMOS 工作点/电容计算、residual/Jacobian stamp、稠密 Newton step 求解, 以及逐 substep 递推,全部在 Rust 内完成;Python 只负责编组、派发和结果组装 (transient_solver.py 自身不再带任何逐步数值循环)。
  • 设置 profile=True 时,原生 BSIM transient 会返回 result["transient_profile"];其中 Newton 总迭代数、原生 BSIM 求值及 batch 调用次数、solver/插入步数、失败步数量及其展开网格索引均由 Rust 热循环直接 统计,并附带实测 solver wall time。每轮 Newton 会解析全部 MOS 端口并调用一次 co_bsim4::eval_batch_into。持久的 EvalBatchWorkspace 在整个 transient 中复用 handle 与 status,求解器同时复用端口行和 I/G/Q/C evaluation 槽;Rayon worker 直接写入这些互不重叠的求解器结果槽,不再需要临时结果向量或适配层复制。专用 线程池默认最多使用 10 个 worker,可通过 CIRCUITOPT_BSIM_BATCH_THREADS=1..10 向下调节。重复的缓存 handle 会归入同一 worker 串行求值,不同 handle 仍可并行,避免并发进入同一可变 compact-model 状态。transient lease 或 campaign worker 会在整个 solve 期间独占这些 handle, 因而热路径可调用内部无锁求值入口;公共标量/C ABI 仍保留逐 handle mutex,batch 内重复 handle 也继续串行。盖章顺序仍保持确定。该池是进程级共享的,因此并行归最外层所有:驱动本身 已在并发求解时(signoff PVT 点、SAR 转换与蒙特卡洛 trial、corner/PVT 切片), 其 worker 会内联求值批次,把核让给外层。(SAR 探索不再并发候选——其 workers 直达每个候选自己的转换 sweep,落在上面"SAR 转换"这一档。)此规则仅在外层任务数不少于 worker 数时生效(与编译式 campaign 的轴策略一致),单次求解仍使用池; CIRCUITOPT_BSIM_NESTED_POOL=1 可恢复原调度,两种调度的数值结果相同。每次 求值都只清零固定 BSIM 矩阵中到 CKTmaxEqNum 的有效行列——那正是 vendor 会 stamp 或读取的全部区域。设置 CIRCUITOPT_BSIM_FULL_FRAME_CLEAR=1 可恢复旧的整帧写入。关闭 profile 时不累计这些计数,也不返回该字段;原 eval_batch ABI 作为兼容包装保留。
  • 未显式提供 V0 的 native transient 会先通过 transient 自己使用的编译 topology、device wrapper 和租用 handle 尝试拓扑声明的 DC guesses。只有 Rust DC Newton 收敛、残差有限且小于 dc_tol,并在拓扑要求时落在 voltage box 内, 才会接受该状态;困难点仍回退到完整 ac_solve。正常路径因此不再重复构建整套 device,也不再执行无人使用的 1 Hz 小信号 reduction。
  • 接受状态的器件电流和电荷历史直接复用最后一轮收敛 Newton 的 Evaluation 数组。收敛分支不会写回小于容差的 correction,因此这些 I/G/Q/C 已与接受状态 对应;失败步仍会在最后一次状态更新后刷新 batch。由此,成功 transient 只需要 一次初始历史 batch 和每轮 Newton 各一次 batch,不再逐步重放接受状态求值。
  • Gear2 会在每次原生 BSIM Newton 求解前使用受保护的变步长状态预测。固定网格 使用两状态线性外推 x[n] + h[n+1]/h[n] * (x[n] - x[n-1]);adaptive Gear2 在三个已接受状态可用后升级为变步长二阶外推,重启后先回退到线性外推。步长增长 超过 4 倍或检测到输入斜率突变时会禁用预测,因此时钟和 DAC 边沿仍以上一接受 状态为初值,同时加速平滑建立段。transient profile 的 gear2_predictor_steps 记录实际启用步数;设置 CIRCUITOPT_BSIM_GEAR2_PREDICTOR=0 可同时关闭两种模式并进行回归 A/B。
  • 原生 BSIM 派发也会在 Rust 内完整执行 adaptive=True。每个候选步只执行一次 Gear2 非线性求解。Rust 核从已接受的 BSIM 端电荷历史构造变步长 BDF3−BDF2 defect,再通过收敛的 BDF2 Jacobian 做一次线性投影。该投影会复用 最后一轮 Newton 的 LU 因子,仅求解第二个右端项,不再重复 stamp 或分解同一个 Jacobian。PI controller 在 LTE 超差或 Newton 不收敛时缩步,增长最多 2 倍,拒步后禁止立即增步,并在 输入斜率断点保守重启。LTE 只对动态节点电压形成;理想源的 MNA 支路电流是代数 乘子,不进入误差范数。返回的 t 是接受后的非均匀网格,profile 额外报告 accepted_stepsrejected_stepstrial_solveslte_estimateslte_linear_solveslte_rejectionsnewton_rejections
  • 对 PSS 常用的非 robust 模式(fallback_least_squares=Falsefallback_full_jacobian=False),compiled grid solver 会留在 Rust 内跑完整周期。 失败 substep 会记为失败 interval,并从最后接受的状态继续,这与非抛错的 transient 行为一致,且不会把整个周期重跑一遍。
  • robust 模式(least-squares 或完整有限差分 Jacobian 恢复)同样是编译核的 调用参数,只在明确请求时生效——不是独立的 Python 数值路径。 result["rust_grid_solver"] / result["rust_adaptive_solver"] 标记某次调用 实际命中了哪条编译路径(已废弃的 numba_grid_solver 结果键已不存在,测试 断言其不出现,而不是断言它为 False)。
  • 使用 PMOS 内部节点的隐式微分加快瞬态 Jacobian 计算,并回退到有限差分。

Gear2/BDF2 积分integration_method="gear2"):瞬态求解器也支持变步长 BDF2(二阶、刚性稳定)。关键特性:

  • 使用稳定的 charge 模式电容伴随模型 i_n = (α0·Q_n + α1·Q_{n-1} + α2·Q_{n-2})/h_n,与 BE 的 (Q_n − Q_{n-1})/h 相同形式但使用两步历史。 另有 per-call 电容模式 override;PMOS chopper PSS wrapper 用 average 算子匹配 Spectre feedthrough,通用 stiff 电路仍保持 charge 默认。
  • 步长比限制 ρ≤2 保证非均匀网格上的零稳定性。
  • 每个 interval 第一步用 BE 自启动。
  • 编译版 Rust gear2 grid 求解器处理 PSS/PAC/PNoise 的周期轨道,也处理 raw transient 的 max_stepflat_max_stepmax_retry_subdivisions;解析 gear2 monodromy(增广 2n 态)供给 PSS shooting Jacobian。
  • adaptive gear2 使用 step-doubling LTE 估计;PSS 会在接近收敛时冻结 accepted grid, 再用该固定 grid 生成最终 orbit/monodromy。编译核(_solve_adaptive_rust, 唯一的 adaptive driver)负责这条路径的加速。
  • 需要可复现 PAC/PNoise 轨迹的 PSS 调用可传入 final_tgrid。此时 adaptive Gear2 只负责 stabilization,且不会冻结 accepted grid;shooting、解析 monodromy、fallback stabilization、profile 和最终返回轨迹都使用指定的 确定性网格。analysis JSON 通过 final_n_points 开启该模式,periodic pulse/square 的边沿边界会同时并入 warmup 与 final grid。
  • transient(integration_method="gear2") 仍是显式 opt-in;当调用方请求 max_retry_subdivisionsmax_step 的 robust 行为时,会留在编译 Rust gear2 grid;grid 在每个 accepted internal substep 后更新 rolling 两步 BDF2 历史,并在 失败时按 2**max_retry_subdivisions 做固定二分 retry。transient_solver.py 没有独立的 Python 数值兜底路径;robust step 被拒绝时直接抛错。
  • Chopper PSS/PAC/PNoise 默认使用 gear2——PAC baseband 误差从 BE 的 −2.5% (typ/fast)改善到三 corner 全部 <1%。
  • transient() 默认仍保留 BE,以保持既有 raw transient 回归和一阶阻尼语义; 默认 BE hard-switched chopper transient 的热路径现在也已完全编译化(Rust), 正常不再触发 Python tail 或 SciPy least_squares

前端激励(ac_drives

对于 testbench,小信号 AC 激励可以通过 Topology.ac_drives(如 {"VINP": +0.5, "VINN": -0.5})施加在节点上,而非器件栅极。驱动通过前端无源网络传播到(现在作为被求解节点的)放大器输入端,增益按差分激励归一化。噪声分析中这些驱动被视为 AC 地(输入端无信号)。examples/afe_testbench.py 在 AFE 核心之前构造了干电极 + AC 耦合前端(R_EL∥C_EL、C_AC 串联、R_AC 到 VCM),并运行 AC(带通约 0.05 Hz–几百 Hz)、等价输入噪声(含 R_EL/R_AC 热噪声)和带内瞬态。由于 AC 耦合输入使裸 AFE DC 多稳态,testbench 从鲁棒的裸 AFE 工作点(dc_seed)作为种子启动 DC 求解。

explore.py

建立在 AC 和噪声求解器之上的设计空间探索/优化驱动——即项目名称所指的"优化"。给定一个电路及 explore 配置(带范围的设计变量、可行性约束和一个或多个目标),它对候选方案进行采样,通过求解器评估每个候选,按约束过滤,并 Pareto 选择权衡前沿。

  • explore(topo, base_sizes, base_bias, nf, cfg, n=, seed=, method=, corner=, workers=)——运行一次扫描。 corner 对每次评估施加工艺偏移(如 CORNERS["slow"]),实现在不修改配置的情况下进行 corner 感知搜索。 满足条件的 BSIM4 电路走由拓扑驱动的 CompiledCampaign:任意 MOS 连接、无源 元件、独立/受控源和增广 MNA 支路只编译一次,geometry/NF/bias 候选再分块进入 一个释放 GIL 的 Rayon 线程池。多稳态 OTFT AFE 只有在 seed_fn 提供一致 DC 种子时才走编译路径;冷启动 AFE 保留标量参考,避免改变物理根。
  • evaluate(topo, sizes, bias, nf, freqs, band, x0_guess=None, corner=None)——单候选求解器评估, 新增可选的 corner/mismatch 参数。在 explore 中评价流程为 AC-first:先计算 gain/BW/power/area,非噪声约束失败的候选会立即淘汰;只有幸存候选的约束或目标 需要 irn_uV 时才运行噪声。编译式 BSIM 路径一次性准备 DC、工作点器件 线性化、已组装 MNA system 和正向 AC 响应,再由可复用 PreparedCampaign 只对幸存候选索引续跑 noise,不重复 DC 或正向 AC。native BSIM handle 保持在 单次调用内部,并在保留的工作点重新偏置,因此 C 指针不会跨 Python 调用或 Rayon worker。
  • load_explore_json(path)——从完整电路 JSON 中读取 explore 块。拓扑、器件尺寸、 偏置和可选 NF 都走同一条 JSON 路径;探索层不再接受旧的 builtin_topology 配置。
  • 采样方式为 lhs(拉丁超立方)或 random,使用带种子的 RNG 保证可重复性。
  • 指标:gain_dBbw_Hzirn_uVpower_uW(所有已求解电源支路的电压电流乘积之和)和 area(各器件 g_area 之和)。
  • 变量的 targets 可以同时驱动多个键值,保持匹配对(M7=M8, …)一致,使 AFE 的对称 DC 续流保持在物理支路上。
  • 结果导出为 CSV 和 JSONL;CLI 运行 python -m circuitopt.explore <config.json>
  • 硅 corner 路由(SKY130_CORNERS/SILICON_CORNERS/apply_silicon_corner)现在在 device_factory.py 里;explore.py 从那里 import,而不是自己定义。
  • add_cli_args(parser) / run_cli(args) 是 CLI 参数定义的单一来源——python -m circuitopt explore 子命令和独立的 python -m circuitopt.explore 入口都调用同一对函数,两个入口不会再互相漂移 (见 cli_reference_zh.md)。
  • explore_from_dict(data, n=, seed=, method=, corner=, workers=, progress=None, should_stop=None)—— explore 子命令和服务层 POST /api/v1/jobs/explore 共用的单一入口:解析 explore 块、 绑定硅 models、再调用 explore()progress(done, total) / should_stop() 是可选钩子, 原样透传给 explore(),供需要实时进度或协作式取消的调用者使用(如服务层的后台任务 管理器,见 service/jobs.py);两者默认 None,结果保持确定性。标量路径在每个 候选前检查取消,编译路径在有界 native chunk 之间检查。提前 停止时 results["stopped_early"]results["summary"]["stopped_early"]Truesummary["evaluated"] 记录实际跑完的候选数(summary["n"] 仍是最初请求的数量)。

示例配置:examples/afe_explore.jsonexamples/single_stage.json,二者都是带 explore 块的完整电路 JSON。

corners.py

工艺角和鲁棒性工作的单一事实来源——这些内容原本会在每次扫描中重复推导。CORNERS 数据本身(全局 工艺偏移 typical / slow / fast,按 pvt0/pbeta0 表示,来源于 PDK 的 monte.scs 段落;如 slow = {"pvt0": -0.2259, "pbeta0": -0.54})现在放在共享的 leaf 器件层 device_factory.py 里; corners.py 从那里 import 它(from .device_factory import CORNERS),所以既有的 from circuitopt.corners import CORNERS 调用点无需改动。corners.py 里保留的是建立在它之上的 mismatch/latch 相关工作:

  • mismatch_corner(rng, devices, base)——在工艺角基础上叠加逐器件随机 mvt0/mbeta0
  • metrics(...)——单设计单 corner → gain_peak_dBbw_Hzirn_uVlatch_dV(DC 工作点的 |out+ - out-|;大值 ⇒ 交叉耦合正反馈已 latch)。
  • corner_table(...)——typ/slow/fast 三个 corner 的指标汇总。
  • latch_screen(...)——确定性最坏情况 latch 筛查:对每个对称对在所有符号组合上施加 ±kσ 推开,返回最大输出失衡。单次固定 kick 存在假阴性(latch 的符号模式依赖于设计),因此筛查遍历所有模式;计算开销足够低,可在搜索内部代替完整 MC 使用。它只需要 DC/AC 工作点和 latch 失衡,因此会跳过噪声。
  • mismatch_mc(...)——单个 corner 上的逐器件 mismatch MC,从名义工作点播种;返回各指标数组、latch 掩码以及汇总(latch 率 + 未 latch 样本的 mean/std/P5/P95)。每个样本先跑 AC/latch,只有进入最终噪声统计的未 latch 样本才计算 IRN。支持可选的 progress(i, n, partial) / should_stop() 钩子(默认 None,结果与加钩子之前逐字节一致):progress 在每个样本完成后触发,带一份轻量的滚动汇总;should_stop 在每个样本开始前检查,若返回 True 则提前结束,在顶层和 summary 里都加上 "stopped_early": True——实际评估完的数量就是 summary["n"](这条路径没有单独的"请求数"字段;与 exploresummary["evaluated"] 不同,mismatch_mcsummary["n"] 本来就始终反映实际跑过的样本数)。
  • mismatch_mc_from_dict(data, n=, seed=, corner="typical", progress=None, should_stop=None)—— mc 子命令和服务层 POST /api/v1/jobs/mc 共用的入口;解析电路后调用 mismatch_mc(), 两个入口因此不会漂移。

ac_solve / noise_analysis 接受相同的 corner 参数(扁平的工艺 dict 或逐器件 mismatch 映射)。驱动脚本 examples/mc_mismatch.py 将其封装为 corner 表 + 3-corner MC 图。

ML surrogate 层(dataset.py / surrogate.py / surrogate_torch.py / optimize.py

把已验证的求解器变成一条完整的 造数据集 → 训练 surrogate → 优化 → 校验 闭环。求解器全程仍是 唯一的 ground truth;surrogate 只加速大候选池的筛选环节。

  • dataset.py ——采样/评估方式与 explore.py 相同,但不做约束/Pareto 过滤,且总是评估 噪声,所以每个样本(含 DC 失败样本)都成为一条带标签的训练行。写出 .jsonl(可读的逐行样本)+ .manifest.json(provenance:schema 版本、solver git commit(+dirty)、拓扑 hash、PDK、models 绑定、 corner、采样 seed/method、变量范围——供下游拒绝域外样本)+ .npz(稠密 X/Y 矩阵,缺失标签为 NaN)+ 可选 .parquet标签组--labels,在默认 ac_noise 之外可选加):transient(复用配置 已验证的 periodic 瞬态得到的、激励无关的波形特征)、pss(周期稳态质量 + 轨道输出)、pac(基带 转换增益 + PAC 网格内 −3dB 角)和 pnoise(带内积分输出/等效输入周期噪声——斩波的核心指标)。 pss/pac/pnoise 三组每候选共享一次 run_analysis_suite 调用,配置 analyses 块里已验证的求解 设置(time_domain、drive、band、打靶容差)原样生效;pac/pnoise 要求配置带对应 analyses 块。 设计轴语法DEV.W/.L/.NF/bias 之外还支持:<Cap>.C / <Res>.R(具名无源器件值——structural,通过 candidate_circuit() 逐候选重建电路)、periodic.frequency(clock)、pvt0/pbeta0(连续全局工艺 偏移——采样它就把离散 corner 扫描变成一个连续 PVT 训练轴)。和 explore.py 一样,dataset.py 的硅 corner 路由(SKY130_CORNERSapply_silicon_corner)也从 device_factory.py import,而不是自己 定义;并暴露同一对 add_cli_args(parser) / run_cli(args),使 python -m circuitopt dataset 子命令与 独立的 python -m circuitopt.dataset 入口共用一份参数定义。
  • surrogate.py ——HistGradientBoostingRegressor(可选 scikit-learn 依赖)逐标签独立训练,对跨 多个数量级的标签(如 IRN)自动用 log-space 拟合。filter_rows() / CLI --filter label:lo:hi 把训练 限制在感兴趣区域内(例如剔除甩轨/collapse 的极端设计——它们的极端标签会拖累平方误差拟合,反正也会被 约束筛掉)。score() 逐标签报告 median/P95 相对误差和 R²。
  • surrogate_torch.py ——可微 MLP surrogate(可选 torch 依赖;Apple Silicon 上支持 MPS),用于 带约束惩罚的多目标梯度优化,附带 --verify 收尾接回求解器。
  • optimize.py ——筛选-校验闭环的落地:用 surrogate 对大候选池做预测(µs/candidate),取约束下的 Pareto 前沿,再把 top-K 送回真实的已标定求解器复核。用 dataset.candidate_circuit()/ split_variables() 保证每种变量(含结构化的电容/电阻/时钟轴)在校验阶段都生效,而不只是 size/bias。

一条关键的经验教训:没有一个 surrogate 能同时是精确的"感兴趣区域" 模型又是懂"失败区域"的好筛选器——用 --filter 只在工作区训练能拿到最紧的指标精度,但会让 surrogate 对甩轨设计一无所知,筛选阶段就分辨不出它们。screen-and-verify 架构正是为容忍这个矛盾设计的:可行性 的最终话语权在求解器,不在 surrogate。

硅 PDK 与紧凑模型后端

三套硅 PDK 都通过与 AT4000TG OTFT 相同的 TransistorModel 接口暴露 BSIM4。 SKY130、FreePDK45 与 TSMC28HPC+ 的正常 DC、AC、noise、transient、PSS、PAC、 PNoise 均使用进程内原生 Berkeley BSIM4 后端。

compact_models/bsim4/card_cache.py 提供三者共用的只读 card 缓存。有界、 线程安全的 LRU 键覆盖完整 PDK/model/section/bin 绑定、几何尺寸、NF/倍乘、 温度、corner、失配、源文件指纹和适配器特有实例字段。它只缓存 PDK/model/instance card,不缓存可变器件对象或运行时端口偏置。按键 single-flight 构建既保留并行 PVT 能力,也避免同一冷 card 被重复展开;模型源的 路径、mtime 或大小变化会使对应派生项自动失效。

compact_models/bsim4/native.py 另行维护有界的 setup 完成可变 native handle 池。 一个不可变 card key 可以对应多个 handle 槽位,使匹配 MOS 实例并发求值时不会共享 内部节点或 limiting 历史。求解器在整条轨迹期间独占租用每个 handle,结束后归还, 后续串行求解可复用 setup 成本和 warm history;活动 handle 永不驱逐。 BSIM4_DEVICE_CACHE_SIZE 限制每个 PDK namespace 的 handle 槽位总数(默认 128), 而不是不同 card key 的数量。 不可变 card 会在构造时预先生成规范化、已排序的参数元组,因此重复租用 handle 不会再次排序整张 model card。该变更只优化 cache key 构造,SKY130、FreePDK45 和 TSMC28 共用此路径。

TSMC28 MDAC signoff 清单只为六个 adaptive residue/code-transition case 显式设置 newton_vtol=3e-8 V。求解器全局默认值仍为 1e-8 V;其他电路必须完成自己的 轨迹和 signoff 对照后再选择是否启用。这六个 case 还设置 bsim_model_bypass_tolerance=3e-9 V,在 Newton 中启用紧凑模型自带的有界标准 bypass。该选项默认值为 0,且不得超过 newton_vtol;每次独占 transient 求值后 都会恢复 handle 设置,scalar 分析和再生式 SAR 仍走精确路径。

  • pdk/sky130/library.py / device.py ——加载随包的按几何展开 BSIM4.5 参数卡,注册原生 sky130.nmos / sky130.pmosextract_w 选择参考宽度卡, 实例仍使用实际几何;缺卡时清晰报错,不自动启动外部仿真器。
  • sky130_model.py ——兼容导出与显式 extract_sky130_card() ngspice 准备工具。正常仿真使用随包卡,不调用该提取路径。

第三个 PDK:FreePDK45 原生适配(pdk/freepdk45/

FreePDK45 现与 TSMC28HPC+ 共用原生 Berkeley BSIM4.5 内核。 pdk/freepdk45/library.py 把平铺的 level-54 VTG 卡解析为数值模型卡; pdk/freepdk45/device.py 通过 freepdk45.nmos / .pmos 提供完整四端电流、 电导、电荷、电容和相关噪声。源卡声明 version=4.0;内置 Berkeley 源码仅把 该字段作为元数据,回归测试以 ngspice 核对单管工作点/噪声和五管 OTA。

原生 host 使用带主元的小型线性求解消去 FreePDK45 的漏源、栅和体电阻内部节点; 同一次 Schur 分解会服务四个外部端子右端项,不再为每个行列组合重复分解内部矩阵。 四端电荷聚合包含分布式体结电荷,并统一 PMOS 电荷符号。因此 N/P 两种极性的 AC 电容与电荷有限差分一致,DC、AC、noise、transient、PSS、PAC、PNoise 可以共用 同一进程内紧凑模型路径。

完整求值会把最终 floating-point model load 已生成的电荷 与小信号字段直接交给 acLoad,省去一次重复 MODEINITSMSIG load;设置 CIRCUITOPT_BSIM_REUSE_SMSIG_LOAD=0 可恢复旧顺序。

实验性开关 CIRCUITOPT_BSIM_REUSE_FINAL_LOAD=1 可让 Newton 更新低于 1 pV 的私有内部节点 线性化直接完成端口消元;由于个别标量 PDK 点会超出公共 API 的位级 golden 契约, 该开关默认关闭。与该实验模式无关,当私有节点解和当前 load 使用值的位模式完全 一致(或不存在私有节点)时,host 总会安全省略 reload。外部/内部节点布局也会在 setup 后缓存;这两项优化保持公共结果逐位不变。

编译式 SAR 单 trial 路径会把输入转换严格分成 min(workers, inputs) 个均衡连续 区间,避免 ceil 分块偶然少于 worker 数时让 campaign Auto 误选串行轴。一次转换 只在开始时渲染完整激励;之后每个 bit 仅更新新激活的试探行,并在需要时更新上一 清零 bit 的行。code-only sweep 不记录端口历史,详细转换则保留完整的已接受 state/current/charge 轨迹。

原生 host 还提供带版本号的守恒求值 ABI、全 void * 入口和批量求值器。 co_bsim4_set_card 可在一次穿越中应用整张 model 或 instance card,vendor 的 关键字表也改为每进程建一次索引、不再逐参数线性扫描;硅工艺 card 有数百个参数, 这两项合起来才使 handle 构造不再位于关键路径上。逐参数的 co_bsim4_set_model / co_bsim4_set_instance 入口仍然保留,并有对拍测试把 整卡路径钉死到它们上。 固定网格 BSIM4 瞬态把矩阵盖章、Newton 与时间步循环整体放在编译 Rust core 内 执行(co-core 进程内直接调用 co-bsim4);compact_models/bsim4/native.py 的 ctypes 绑定是 Python 侧调用同一份编译库做单点 op/AC/noise 求值的入口。 v2.0.0 起生产路径不再有独立的 Python 数值实现。

  • pdk/freepdk45/library.py ——可迁移卡路径解析、严格绑定/corner/极性校验、 数值解析与公共只读 card 缓存。
  • pdk/freepdk45/device.py ——原生 TransistorModel 适配器和默认 freepdk45.* 注册。
  • freepdk45_model.py ——兼容导出与可选的旧 freepdk45_ngspice.* 网格别名。
  • ngspice_char.py / ngspice_device.py / ngspice_transient.py ——保留为外部 回归 oracle 基础设施,不再是 FreePDK45 默认运行路径。

TSMC28HPC+ 原生适配(spice/ / compact_models/bsim4/ / pdk/tsmc28/

内部 HSPICE 前端负责 .lib/.include 闭包、参数表达式、子电路、foundry MOS macro 和尺寸 bin。TSMC28 library 层为每个实例选择 0.9V nch_mac / pch_mac core 模型, 再把展开后的模型参数和实例参数交给原生 Berkeley BSIM4.5 后端。

原生器件提供四端电流、电荷、电导、电容和相关噪声,因此 DC、AC、noise、transient、 PSS、PAC、PNoise 都不需要 ngspice 子进程。旧工艺适配器仅以显式 tsmc28hpcp_ngspice.* 名称保留,作为独立回归 oracle。

默认可迁移模型入口为 PDK/tsmc28hpcp/models/hspice/cln28hpcp_1d8_elk_v1d0_2p2.l,且被 Git 忽略。 解析优先级为 TSMC28_MODEL_DIRTSMC28_PDK_ROOT、项目内入口、 PDK_ROOT/tsmc28hpcp。详见 TSMC28HPC+ 适配说明

circuitopt/circuit_loader.py 要求 JSON 中每个 MOS 都有显式 models 条目 (pdkmodelsectionbin),从而把器件明确绑定到对应 PDK, 所以一个混合 OTFT+硅(或全硅)电路只是配置 问题——见 JSON 电路格式。两个完整的全差分 OTA 设计流程案例: SKY130 FD-OTAFreePDK45 FD-OTA

本地服务层(service/app.py / jobs.py / serialize.py / cli.py

架在整个求解器栈之上的可选本地 FastAPI HTTP 层,由 serve extra 门控 (pip install -e ".[serve]")。它是一层薄适配——每个路由都调用 service/operations.py 中与 transport 无关的单一实现,本身不带任何数值逻辑。 完整端点参考见 本地服务 API

  • app.py —— create_app(job_workers=1) -> FastAPI 构建 /api/v1 app:GET health/capabilitiesPOST validate/solve(同步,直接调用 circuit_from_dict/validate_analysis_cfg/run_analysis_suite),以及由 jobs.JobManager 支撑的 jobs/* 后台任务路由(POST jobs/explore/jobs/mcGET jobs/jobs/{id}DELETE jobs/{id}WS jobs/{id}/events)。CORS 只放行 localhost/127.0.0.1 任意端口。 pydantic 请求模型(SolveRequest/ExploreJobRequest/McJobRequest)把 circuit 当作不透明 dict 透传——电路 schema 的单一事实来源始终是 circuit_from_dict,这里不重新描述一遍。
  • jobs.py —— JobManager/Job:为两个长任务驱动函数(explore_from_dictmismatch_mc_from_dict)建立的进程内 ThreadPoolExecutor 后台任务表。状态机 queued -> running -> {done, failed, cancelled};进度被推到逐 job 的 queue.Queue (由 WebSocket 路由消费),同时缓存在 Job.progress 供轮询读取;取消操作设置一个 threading.Event,作为 should_stop 回调传给核心驱动函数(协作式——正在跑的候选点/样本 总会先跑完)。内存最多保留 MAX_JOBS(50)个任务,优先驱逐最旧的已终结任务。不 import fastapi——纯 threading/queue 管道代码,可独立单测。
  • serialize.py —— to_jsonable()/serialize_results():numpy/complex/NaN → 严格 JSON 的转换约定,被所有响应共用(同步端点和 job/WebSocket payload 都走它)。NaN/±Inf → nullcomplex{"re", "im"}numpy.ndarray → 嵌套 list_ 前缀的 dict key 和 callable 被丢弃。
  • cli.py —— add_cli_args(parser)/run_cli(args)serve 子命令参数定义 (--host/--port/--reload/--job-workers)的单一来源,被 circuit-opt serve 子命令和独立 的 python -m circuitopt.service 入口共用,与 explore/dataset 的单一来源 CLI 模式一致。 run_cli 内部延迟 import fastapi/uvicorn,所以 circuitopt/__main__.py(为了注册子命令会 提前 import circuitopt.service)不需要装了 serve extra 才能正常工作。

MCP 层(mcp/server.py / workspace.py / cli.py

可选 MCP 适配层由 pip install -e ".[mcp]" 门控,支持 stdio 和只允许 loopback 的 Streamable HTTP。它与 FastAPI 调用同一组 service.operations,额外负责工作区相对 路径隔离、直接响应中的长向量压缩,以及把完整结果写入 results/mcp。探索、失配 MC 和 signoff 都作为可取消的 JobManager 任务运行。完整工具和 resources 见 MCP 服务

快速示例

import numpy as np

from circuitopt.ac_solver import ac_solve
from circuitopt.noise_solver import noise_analysis, band_rms
from circuitopt.transient_solver import transient

sizes = {
    "M6": (2264, 78),
    "M7": (61365, 61),
    "M8": (61365, 61),
    "M9": (3175, 468),
    "M10": (3175, 468),
    "M11": (465, 66),
    "M12": (894, 85),
    "M13": (894, 85),
    "M14": (5224, 46),
    "M15": (5224, 46),
}

bias = {
    "VDD": 40.0,
    "VCM": 30.65,
    "VB": 9.84,
    "VC": 16.0,
}

freqs = np.logspace(-2, 4, 121)

ac = ac_solve(sizes, bias, freqs)
noise = noise_analysis(sizes, bias, freqs)
irn_uv = band_rms(freqs, noise["irn_psd"], 0.05, 100) * 1e6

t = np.linspace(0, 4e-3, 400)
vip = np.where(t >= 0.5e-3, bias["VCM"] + 0.5e-3, bias["VCM"])
vin = np.where(t >= 0.5e-3, bias["VCM"] - 0.5e-3, bias["VCM"])
tran = transient(sizes, bias, t, vip, vin)

JSON 电路示例

新电路可以从 JSON 加载。字段级格式见 JSON 电路描述格式

import numpy as np

from circuitopt.circuit_loader import load_circuit_json
from circuitopt.ac_solver import ac_solve
from circuitopt.transient_solver import transient

spec = load_circuit_json("examples/single_stage.json")
freqs = np.logspace(0, 4, 121)

ac = ac_solve(spec.sizes, spec.bias, freqs, topo=spec.topology, nf=spec.nf)

t = np.linspace(0, 1e-3, 100)
vin = np.full_like(t, spec.bias["VIN"])
tran = transient(spec.sizes, spec.bias, t, topo=spec.topology,
                 nf=spec.nf, inputs={"vin": vin})

基准测试

benchmarks/ 下有五个基准:

# 全 AFE 基准(ac121 / noise121 / tran200)
python3 -m benchmarks.bench_afe --warm-runs 3

# 单管 PMOS_TFT 微基准(7 个热路径操作 × 3 个偏置工作区)
python3 -m benchmarks.bench_model --warm-runs 3

# Chopper 分析基准(harmonics / ideal / pmos_static / pmos_lptv / pmos_tran)
python3 -m benchmarks.bench_chopper --warm-runs 3
python3 -m benchmarks.bench_chopper --skip-tran --warm-runs 3

# 周期 PSS / PAC / PNoise 基准
python3 -m benchmarks.bench_periodic --warm-runs 3

# 批量 sweep 基准(N × AC / AC+noise,模拟 explore 层负载)
python3 -m benchmarks.bench_sweep --n-candidates 200 --warm-runs 3

bench_afe.py 报告三种全 AFE 负载的 cold/warm 耗时。bench_model.py 测量单管 操作的性能(DC OP、Idc、电容、噪声 PSD、Cadence 指标),覆盖饱和区、亚阈值区和 线性区三个偏置点。bench_chopper.py 覆盖五个 chopper 分析层级,使用 f_chop=225 Hz ——从快速的有限边沿谐波计算(~1 ms),到理想 LPTV 折叠、PMOS 静态相位、 准静态 PMOS 边带折叠,以及最重的 hard-switched PMOS chopper 瞬态。bench_sweep.py 测量 N 个随机扰动候选的 AC / AC+noise 批量吞吐量,模拟 explore 层的逐候选 评估负载。v2.0.0 起 CIRCUIT_ENGINE 只接受 rust,已没有其他引擎可比较 (v1.x numba/解释执行基线见 docs/environment_performance.md)。

旧 UI chopper 全流程瓶颈是通用 HB PAC frequency solve:显式 PSS+PAC(HB)+PNoisetime_domain=False)61 点约 25.6s(PSS≈0.35s、 PAC≈24.7s、PNoise≈0.55s),121 点约 48.9s(PSS≈0.44s、PAC≈47.6s、 PNoise≈0.93s)。当前默认 chopper time-domain PAC 保留 PMOS gate1 状态, 并使用所选引擎的编译转换装配;同一 PSS 轨道上 61 点约 1.4s。非 chopper AFE 的 DC+AC+Noise 121 点在复用 AC 结果时约 1.8ms。

校准状态

当前核心已针对 AT4000TG AFE 用例在 Cadence Spectre 24.1 上完成校准。原始项目中观察到的吻合度包括:

  • 典型和 corner AC 行为增益误差约 0.01 dB 以内。
  • 已验证场景中等价输入噪声误差在百分之几以内。
  • 逐器件 mismatch Monte Carlo 的均值和标准差与 Cadence 趋势一致。
  • 瞬态阶跃和正弦响应与 Cadence tran 行为高度吻合。
  • PMOS 八开关 chopper finite-edge transient 已按 UI 锁定尺寸、f_chop=225 Hz、 switch W/L=5000/30rise/fall=20 us 与 Spectre tran 对齐;默认 edge_time/10 内部步长下,最后一周期输出均值约 -10.76 mV (Spectre -10.62 mV),输出 21.11 mVpp(Spectre 21.46 mVpp), 输入 common-mode 摆幅 5.14 Vpp(Spectre 5.43 Vpp),且 nfail=0
  • PMOS 八开关 chopper PSS/PAC/PNoise 已按同一 UI 锁定案例与 pmos_chopper_lptv_analysis(...) 对齐:gain 21.370 dB 对 Spectre 21.369 dB,带宽 738.6 Hz721.9 Hz,IRN 12.592 µVrms12.591 µVrms
  • 原生 pmos_chopper_pacpmos_chopper_pnoise(第一性原理,无标定常数)已 与 D3 / chop_tb_d3 官方 slow corner Spectre PSS/PAC/PNoise 对齐: f_chop=200 Hz 时默认 time-domain PAC 约 +0.03%,TD-adjoint PNoise IRN 约 +0.02%。 slow/typical/fast 三 corner 的旧 HB-K32 IRN 误差为 +1.81% / +1.05% / +0.66%, TD PNoise 后为 +0.02% / −0.00% / +0.57%。
  • SC-LPF calibration 只用 adaptive Gear2 完成 stabilization,随后在并入全部 时钟边沿的 3201 点基础确定性网格上完成 shooting,并返回 PAC/PNoise 轨迹。 它保留 cap_mode="average",PNoise 使用 512 点重采样和 pnoise_max_sideband=20。对入库 Spectre 参考,PAC 增益、带宽与积分输出 噪声误差分别约为 0.5%、1.2% 与 2.3%。
  • 最终锁定设计约 22.9 dB 增益、549 Hz 带宽、37 µVrms 等价输入噪声。

上述数据描述当前的 AT4000TG 验证案例。后续 PDK 或拓扑应针对其各自的仿真器参考重新进行校准。