核心求解器概览¶
文档状态:持续维护的架构参考。 模块职责和数据流保持更新;文末性能与校准数字 属于带日期的快照,在新报告中引用前应重新运行对应基准。
本文介绍当前 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
这张图上有两条性质值得单独说明,因为二者都是承重的:
explore、corners、dataset、chopper直接调用求解器,不经过analysis_dispatch。dispatch 负责让一个电路跑完一套配置好的分析;这四个模块 驱动的是候选矩阵,需要直接拿到求解器。service/与mcp/是纯消费叶子。 二者之外没有任何模块反向导入它们,且circuitopt/__init__.py从不导入其中任何一个,因此即使装了serveextra, 普通的import circuitopt也不会引入 fastapi。
下面的完整列表只记录模块级导入。有两对模块——run_contract/dc_measurements
与 sar/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 参数,如
pvt0、mvt0、pbeta0和mbeta0。 - 带热启动的内部节点工作点求解。
- 标量内核为编译执行:器件方程在
circuitopt_core.OtftModel中求值 (v2.0.0 起为唯一引擎)。
AC 和噪声分析时,求解器通过有限差分 get_Idc 提取端 gm 和 gds,与电路求解器使用的端行为保持一致。
PMOS_TFT 继承 :class:~device_model.TransistorModel,即所有求解器消费的抽象基类。
它还提供 get_otft_params() 供瞬态求解器编译内循环使用,
以及由编译 Rust core 解析端导数支撑的 get_ss_params() 覆盖方法。
device_model.py¶
定义抽象器件模型接口,将求解器与具体晶体管实现解耦:
TransistorModel(ABC) — 七个抽象方法(get_Idc、get_op、get_capacitances、get_capacitance_charges_from_op、get_capacitance_branch_terms_from_op、get_noise_psd、get_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 器件设为True。TRANSIENT_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_types、device_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.py和dataset.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):显式传给求解器的非Nonekeyword 永远优先;否则由 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_suite、explore、dataset、optimize——统一穿一个 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/Rstamp,电容按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,包含:
topologysizesbiasnf
这使得可以通过 JSON 文件(如 examples/single_stage.json)添加新电路,而无需修改求解器源码。CircuitSpec.binding() 把 spec 的 topology、model_types、device_kwargs、nf 以及默认 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.py 的
run_analysis_suite(以及 JSON schema 回归测试)都从它派生,故 solver kwargs/
默认值/schema 不会互相静默漂移。validate_analysis_cfg(analysis, cfg) 拒绝
JSON analyses 块里的残留键:known_keys(analysis) 把该 solver 的选项注册表
和 DISPATCH_KEYS(run_analysis_suite 直接从 cfg 里读、而不转发进
solver_kwargs 的少数键——如 ac/noise 的 freqs/corner/band,
transient 的 signed_devices)取并集;任何键落在这个并集之外都会抛
ValueError,点名该分析名、出错的键、以及排序后的合法键列表。这把一次拼写
错误(比如把 max_sideband 拼成 max_sidebands)从"静默用默认值跑掉"变成
立即报错。
analysis_dispatch.py 里的 ANALYSIS_ORDER = ("ac", "noise", "transient", "pss",
"pac", "pnoise") 是权威的分析名元组与执行顺序;run_analysis_suite 遍历它,
服务层 GET /api/v1/capabilities 的 analyses 映射也遍历同一个元组来构建,
两处列出的分析名因此天然一致,不需要另外维护一份硬编码列表。
psf.py¶
provenance(path)["fundamental"] 读取 PSF HEADER 里 "fundamental
frequency" 键(若某个非标准写入器只给出裸 "fundamental" 拼法则回退到它)——
周期性分析(PAC/PNoise/PSS)会报出真实驱动频率;DC/AC/noise/tran 两个键都没有,
读回 None。parse_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_TFTpass 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_pss→pmos_chopper_pac/pmos_chopper_pnoise)。 原来那两个 Cadence 拟合常数(换向相位/噪声 PSD scale)已retire;conversion_phase_rad/periodic_noise_psd_scale仍保留为手动可调参数。pmos_chopper_transient(...)用有限边沿 clock 驱动八 PMOS 拓扑。默认 clock 采用 Spectretype=pulse语义(delay=T/2、width=T/2、有限rise/fall); 旧的居中相位波形仍可通过clock_style="phase"使用,适合 dead-time 实验。 clock feedthrough 来自 PDKCgss/Cgdd * ddt()项以及 PDK Verilog-A 中长期有效的R_cap2gate-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_cap、R_cap2、Cgs、Cgd),不再逐时刻塌缩成端口{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_solvefast 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_pac 和 pmos_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周期性重建。 - 返回一周期轨迹、
x0、x_end、残差向量/范数、收敛标志和迭代历史。 结果中还包含shooting_period_runs、shooting_jacobian_evals和shooting_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_solve 和 pnoise_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 结果包含
topology、t、nodes、x0、x_end、output以及周期输入波形元数据。 input_drive是小信号复幅值映射,例如差分输入为{"vip": 0.5, "vin": -0.5}, 单端输入为{"vin": 1.0}。- 四条性能路径,按优先级依次尝试:
- LTI fast path — 静态 PSS 轨道直接退化为普通
ac_solve。 - Time-domain Floquet PAC(
time_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(边带数)控制。 - 有限差分 shooting(
analytic=False)— 有限差分状态转移矩阵 Φ 和 复输入扰动,求解(Φ-γI)dx0=-b。每频点需n_state+2次瞬态运行。 结果缓存在pss_result上供重复调用。 - 结果中包含
pac_period_runs、pac_state_cache_hit、pac_input_cache_hits和pac_td_setup_time_s等计数器,以及method字段 ("pss_time_domain"、"pss_analytic_adjoint"或"pss_fd_shooting")。 PAC condition 诊断默认关闭,只会在profile=True、debug=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 循环开销主导。 - 如果未显式传入
gains或pac_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_step、max_retry_subdivisions、fallback_full_jacobian和fallback_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_reltol、adaptive_vabstol、adaptive_iabstol、adaptive_max_steps和adaptive_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_batchABI 作为兼容包装保留。 - 未显式提供
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_steps、rejected_steps、trial_solves、lte_estimates、lte_linear_solves、lte_rejections和newton_rejections。 - 对 PSS 常用的非 robust 模式(
fallback_least_squares=False且fallback_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_step、flat_max_step和max_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_subdivisions或max_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 或 SciPyleast_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_dB、bw_Hz、irn_uV、power_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"]为True,summary["evaluated"]记录实际跑完的候选数(summary["n"]仍是最初请求的数量)。
示例配置:examples/afe_explore.json 和 examples/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_dB、bw_Hz、irn_uV和latch_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"](这条路径没有单独的"请求数"字段;与explore的summary["evaluated"]不同,mismatch_mc的summary["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_CORNERS、apply_silicon_corner)也从device_factory.pyimport,而不是自己 定义;并暴露同一对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.pmos。extract_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_DIR、TSMC28_PDK_ROOT、项目内入口、
PDK_ROOT/tsmc28hpcp。详见 TSMC28HPC+ 适配说明。
circuitopt/circuit_loader.py 要求 JSON 中每个 MOS 都有显式 models 条目
(pdk、model、section、bin),从而把器件明确绑定到对应 PDK,
所以一个混合 OTFT+硅(或全硅)电路只是配置
问题——见 JSON 电路格式。两个完整的全差分 OTA 设计流程案例:
SKY130 FD-OTA、FreePDK45 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/v1app:GET health/capabilities、POST validate/solve(同步,直接调用circuit_from_dict/validate_analysis_cfg/run_analysis_suite),以及由jobs.JobManager支撑的jobs/*后台任务路由(POST jobs/explore/jobs/mc、GET 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_dict、mismatch_mc_from_dict)建立的进程内ThreadPoolExecutor后台任务表。状态机queued -> running -> {done, failed, cancelled};进度被推到逐 job 的queue.Queue(由 WebSocket 路由消费),同时缓存在Job.progress供轮询读取;取消操作设置一个threading.Event,作为should_stop回调传给核心驱动函数(协作式——正在跑的候选点/样本 总会先跑完)。内存最多保留MAX_JOBS(50)个任务,优先驱逐最旧的已终结任务。不 importfastapi——纯 threading/queue 管道代码,可独立单测。serialize.py——to_jsonable()/serialize_results():numpy/complex/NaN → 严格 JSON 的转换约定,被所有响应共用(同步端点和 job/WebSocket payload 都走它)。NaN/±Inf →null,complex→{"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内部延迟 importfastapi/uvicorn,所以circuitopt/__main__.py(为了注册子命令会 提前 importcircuitopt.service)不需要装了serveextra 才能正常工作。
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)+PNoise(time_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、 switchW/L=5000/30、rise/fall=20 us与 Spectretran对齐;默认edge_time/10内部步长下,最后一周期输出均值约-10.76 mV(Spectre-10.62 mV),输出21.11 mVpp(Spectre21.46 mVpp), 输入 common-mode 摆幅5.14 Vpp(Spectre5.43 Vpp),且nfail=0。 - PMOS 八开关 chopper PSS/PAC/PNoise 已按同一 UI 锁定案例与
pmos_chopper_lptv_analysis(...)对齐:gain21.370 dB对 Spectre21.369 dB,带宽738.6 Hz对721.9 Hz,IRN12.592 µVrms对12.591 µVrms。 - 原生
pmos_chopper_pac和pmos_chopper_pnoise(第一性原理,无标定常数)已 与 D3 /chop_tb_d3官方slowcorner 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 或拓扑应针对其各自的仿真器参考重新进行校准。