net.nominal_voltage

a RAIL's nominal voltage derived from its net name (3V3 -> 3.3). Rails only; a non-rail net's name-derived level is net.signal_level

What it is

net.nominal_voltage(net, volts) yields the nominal voltage a rail’s NAME declares, so +3V3 → 3.3, 5V → 5, 1V8 → 1.8. It reads only the net name (the same token grammar net.max_voltage falls back to), so a net whose name carries no parseable voltage token, or whose tokens disagree (12V_TO_5V), yields no row: the relation refuses to guess. Every row cites the net’s IR source.

Rails only. The net must carry the rail role (ground counts) before its name is read. A non-rail net whose name declares a level is on net.signal_level instead, and the two relations are exhaustive and disjoint over the nets whose names parse.

That gate was missing until agni issue 194, and its absence is worth knowing about because the relation still worked: the projector emitted a row for any net whose name parsed, so a team encoding a signalling level into a signal net’s name got that level back from a relation meaning “rail nominal”. A net named U3_12_U7_4_3V3 yielded 3.3 while classifying as neither rail nor ground. The number was right and the relation carrying it was not.

A net named +3V3 yields the nominal 3.3V; a net named VBAT carries no voltage token and yields no row

For hardware engineers

This is the design’s own statement of what a rail is supposed to be, taken from how the schematic names it. It is the label rather than a measured or computed worst-case voltage. That is enough to compare a rail against a datasheet’s recommended operating window: if a part wants 3.0–3.6 V on VDD and the net feeding it is named +5V, the nominal alone tells you the rail is out of range. A rail named without a voltage token (VBAT, VBOOST) simply has no nominal here.

For software engineers

net.nominal_voltage is the design-side scalar a datasheet range check joins against. It is deliberately split from net.max_voltage: net.max_voltage prefers an explicit max_voltage attribute and only falls back to the name, so it can carry an engineer-annotated worst case; net.nominal_voltage is name-only, the plain nominal, never the annotated max. Keeping them separate means a rule states which evidence it wants. The projector wraps nominalVoltageFromName, the same function railMaxVoltage and the rail rules already use.

The rail gate is on the relation, not on that function. check.NominalVoltageFromName takes a bare name and has no net to ask about a role, so it cannot gate and does not try. A Go rule that holds a net and means rails has to gate for itself with Model.IsRailNet; the pin-tracking rules are the worked example. So the fact and the Go rules agree on the number but not automatically on the SET, and that asymmetry is the thing to remember when authoring a rule against this relation.

Go projector

netNominalVoltageFacts in check/facts.go iterates Model.Nets() and, for each net whose name parses to a nominal via nominalVoltageFromName, emits one row with Num set to the voltage and a citation to the net’s IR provenance. Nets with no parseable name-nominal are skipped. This relation is netlist-tier: it needs no --params and is populated on any read design.

Datalog

List every rail with a name-derived nominal:

net.nominal_voltage(?net, ?v) => ?net, ?v

Join to a datasheet recommended window (needs --params) to compare a rail against the part it feeds. See param.range:

component-on-net(?ref, ?net), net.nominal_voltage(?net, ?v),
component.mpn(?ref, ?mpn), param.range(?mpn, ?sym, "recommended_operating", ?min, ?max),
?v > ?max => ?ref, ?net, ?v