Compiled Verilog-A Models
Running a compact model you supply — a published physics-based transistor model, or one you wrote — from Verilog-A source or from a compiled artefact, with no kit and nothing to install inside circuitRF.
What this component is for
The VerilogA component runs a compact model you supply. It needs no kit, no manifest and nothing installed into circuitRF — you point it at a file and place it.
That is the path to take when the device you need is a published physics-based compact model. Those
are distributed as Verilog-A source: a few thousand lines of analog equations, a manual, and one
or more fitted parameter sets. What this page buys you is the whole of that route — source in, a
placed five-terminal device out, running DC, S-parameters and harmonic balance like any other
component.
The File parameter takes either Verilog-A source (.va,
.vams) or an already-compiled model (.osdi). Source is compiled once,
by the compiler on your machine, and reused. If you already have a compiled artefact, point at it and
no compiler is involved at all.
The compiler, and why it is yours
circuitRF does not contain a Verilog-A compiler and never will. The established compilers for this model format are GPL-3.0; circuitRF is MIT, and bundling or linking one would change that. So circuitRF does what a build system does with a C compiler: it runs a compiler you installed, as a separate program, and loads the file that comes out. Nothing is linked and nothing is redistributed.
Install one of your choosing, then either put it on PATH — in which case there is nothing to
configure — or name it explicitly:
Settings ▸ Security & Permissions ▸ Verilog-A Compiler
Leave the box blank to use whatever is on PATH. Fill it in when you have more than one, or when
yours lives somewhere PATH does not reach. A compiler you name wins over PATH — that is the
whole point of naming one.
Blank also looks in the places a compiler is usually installed — ~/.local/bin, /opt/homebrew/bin
and /usr/local/bin — because an application started from the Finder or a desktop launcher does not
inherit the PATH your terminal has. So a compiler you can run by typing its name is found here
too, without your having to name it. Press Test and circuitRF will run it and report what it says it is,
which is the fastest way to confirm the setting before you need it.
For a headless run — a script, CI, a batch job — set CRF_VERILOGA_COMPILER to the compiler's path.
It outranks PATH and leaves nobody's settings file touched.
If no compiler can be found, circuitRF says so at the moment you choose the file, names what to
install and where to point it, and changes nothing else. The .osdi route keeps working.
A worked example, end to end
This is the whole path for a model you have just downloaded. It assumes only that you have the model's source and one of its parameter sets — which is what a model family actually ships.
- Put the source where you want it. Keep the family's own folder layout intact: models of
this size
`includeparameter and macro files beside the source, and circuitRF passes the source's own directory to the compiler as an include path so those resolve exactly as they do when you build by hand. - Place a VerilogA component. It is in the palette under Devices — search for "Verilog-A", "OSDI" or "compact model".
- Set
File. Click Browse… beside theFilerow and choose the.va. circuitRF compiles it — once, taking a few seconds for a model of this size — and then reads it. The parameter editor tells you underneath which compiler ran, where the artefact was written, and whether anything was actually rebuilt; on every later visit it will say the source has not changed.If the compiler refuses, you get its own diagnostics, verbatim: file, line and column, exactly as it printed them. That is deliberate — the line number is the whole value of a compiler error, and a paraphrase would be worse than the error.
- Check
Model. A file usually declares one module and circuitRF fills this in for you. When it declares several, the row becomes a picker — choose the one you want. - Check
Pins. circuitRF fills this in from the model's own terminal count and then locks it, because the model has stated it. See below for the one case where you deliberately set it lower. - Load the parameter set — Load Parameters… at the foot of the parameter editor. See below.
- Wire it and run. From here it is an ordinary nonlinear component. Attach a DC analysis to sweep a bias point, S-parameters to get small-signal behaviour about that point, and harmonic balance for large-signal power, gain and efficiency. Everything in Simulations applies unchanged, and so does harmonicaRF — a compiled model is a valid DUT there too.
Run a DC sweep before anything else. A compact model that is mis-parameterised, or whose thermal terminal is wired the wrong way, shows it as a bias point that will not converge — and that is far easier to read at DC than in the middle of a harmonic-balance sweep.
Terminals, Pins, and the thermal one
The symbol is a plain box, deliberately. circuitRF does not know what your model is — it could be a transistor, a diode or a whole subcircuit — so drawing a transistor glyph would assert something the file has not said.
The leads are named by the model. Once circuitRF has read the file, each lead is labelled with the
model's own name for that terminal — d, g, s, b, dt — rather than 1..5. On a five-terminal
part with five identical leads, numbers are the single largest source of mis-wiring, and the model has
already told circuitRF which is which. A lead the model does not name falls back to its number, on its
own; you may see a mix.
The thermal terminal
Many physics-based models expose a thermal terminal: a node whose "voltage" is a temperature and whose "current" is a power. You have two honest choices, and circuitRF supports both.
| What you draw | What the model does | When to use it |
|---|---|---|
Pins = the model's full count, thermal terminal wired |
Reads the temperature you impose on that node | You are building the thermal network yourself — a resistance and capacitance from that node to an ambient source that sets the baseplate temperature. |
Pins = one less, thermal terminal omitted |
Sees that the terminal is unconnected and grounds it internally | The ordinary case. The model handles its own self-heating and you draw a four-pin part. |
When you set Pins one below the model's count and the omitted terminal is thermal, the parameter
editor says so — that a thermal terminal is deliberately left off and the model is handling its own
self-heating. It is not a warning. It is there because a symbol with one lead fewer than the model
declares otherwise reads as a mistake, and "fixing" it is the one thing you must not do:
A thermal node with a pin on it and no thermal network attached is a floating node with no DC solution. It does not fail quickly or clearly — it spends the whole continuation budget and then reports a residual. Either omit the pin (and let the model ground it) or attach a real thermal network. There is no third option.
Loading a fitted parameter set
A fitted parameter set for a model of this size is 50 to 200 numbers. Both of the ways to get them in are at the foot of the parameter editor:
- Add Parameter… — a searchable list of everything the model declares, with the model's own units, descriptions and defaults. Adds one, seeded at the model's own default. Use it to change a handful of values.
- Load Parameters… — reads a whole fitted set from a file. Use it for anything larger.
Load Parameters… reads sets written as Verilog-A parameter declarations, which is the form these families actually ship:
parameter real vxo = 1.3e7; // saturation velocity
parameter real beta = 1.8;
parameter integer nf = 4;
Comments are ignored (including one containing a semicolon), from […] and exclude ranges are
ignored, and both 1.3e7 and engineering notation like 2.4p are read as written. The whole load is
one undo step.
Two behaviours worth knowing:
- Case is aligned to the model's own spelling. A set written
vxoreaches a model that declaresVXO. The match is case-insensitive only — a genuine typo is not quietly turned into something the model accepts; it is reported by name. - Names the model does not declare are reported, never dropped in silence. circuitRF tells you exactly which ones did not land. This is the common case, not the exotic one: a set written for a different version of the same family will have names that have since been renamed or removed, and a silent drop would leave the device running on the model's own defaults for all of them and looking perfectly healthy.
circuitRF only writes the parameters your set actually assigns. A parameter that is not on the component is not sent to the model at all, which already means use your own default. That is why the whole declared list is never materialised as rows: freezing today's defaults into the design would mean recompiling with a changed default silently had no effect.
Reading back what the model computed
A compact model computes far more than the currents it hands the solver. Transconductances, junction
capacitances, an overdrive voltage, a junction temperature — a physics-based model declares tens of
them, and they are the numbers a designer actually reasons about. Verilog-A calls them operating
point variables; circuitRF reads them back after every solve and publishes them as a result cube
named OP.
They are never parameters. A parameter is something you tell the model; an operating-point variable is something it tells you, and it does not appear in the parameter editor for the same reason a voltmeter has no dial.
What you get, and where
At a DC operating point each one is a number, on a labelled axis whose entries are
<instance>.<name> — X1.gm, X1.cgs, X1.tj. Wrapped in a parametric sweep it becomes a curve
per quantity, one point per bias, in the Data Display's signal list beside V and I.
At a harmonic-balance point each one is a waveform, because that is what it is: a
transconductance at 30 dBm of drive swings between pinch-off and full channel once per RF cycle, and
no single number describes it. So OP there is a spectrum on the same harmonic axis V and I
use — harmonic 0 is the cycle average, which is what "gm at this drive" usually means, and the
higher entries say how hard the quantity is being swung.
In a measurement, name one through the analysis it came from:
measure gm_mS = DC1.OP("X1.gm") * 1000
measure gm_avg = HB1.OP("X1.gm", 0)
A name the model does not declare is refused, by name, with the available ones listed — it is never quietly dropped.
The parameter editor's Report operating-point variables checkbox is on by default. A model declaring forty quantities, on a design holding a dozen such devices, swept over a few hundred bias points, is a result carrying thousands of names — so turn it off on the devices you are not studying. Off costs nothing at all: the device is not asked, so neither the round trip nor the cube happens.
Two kinds of quantity are declared and not plotted. A model may declare an op-var as an integer
or as a string. An integer arrives as an ordinary number. A string cannot: a result cube holds
numbers of one kind, and there is nowhere in one to put a word. Such a quantity is still reported as
declared, so you can see that it exists; it simply never appears in OP.
When circuitRF rebuilds, and where the artefact goes
A simulation of an unedited model compiles nothing. The compiled artefact is cached, and the cache key is:
- the content of the source — not its path and not its timestamp, so re-saving a file without changing a character costs nothing;
- the content of every file it
`includes, so editing a parameter or macro file beside the source does rebuild — hashing only the top file would hand you a stale artefact that runs and looks correct; - the compiler's own identity, so upgrading your compiler rebuilds even though your source has not changed by a byte.
Change any of those and you pay exactly one recompile. Change none of them and the compiler is not asked to build anything, however many times you run.
Artefacts are written to circuitRF's own per-user cache — not beside your source. A model family is often installed as a read-only tree, and writing build output into someone else's delivery is wrong even where it would succeed. The parameter editor names the exact path each time it reads the file, so a rebuild you were not expecting is visible rather than mysterious.
What is not supported
- No noise analysis from a compiled model.
- Aging and degradation parameters are forwarded to the model like any other parameter and then do nothing — there is no aging analysis and no stress history to drive one.
- Verilog-AMS digital constructs are the compiler's business, not circuitRF's. If your compiler builds the file, circuitRF will run it.
| Net | Terminal |
|---|---|
| 1 | 1 |
| 2 | 2 |
The nets of an instance line, in this order. How many there are is set by Pins; the table shows Pins = 2.
| Name | Default | Unit | On schematic | Meaning |
|---|---|---|---|---|
| File | — | — | shown | The model to load: a compiled model (.osdi), or Verilog-A source (.va, .vams) which circuitRF builds once with the compiler installed on this machine and reuses until the source changes. Choosing one fills in Model and Pins below. |
| Model | — | — | — | Which device type inside that file to place. A file usually declares one, and then this can be left blank; when it declares several, pick the one you want. |
| Pins | 2 | — | — | How many terminals the symbol draws. It is the model's own terminal count, filled in from the file — change it only if you are drawing before choosing one. |
| OpVars | true | — | — | Whether this instance publishes the operating-point variables its model computes — transconductances, capacitances, node temperatures. On by default; turn it off on devices you are not studying to keep a swept result small. |