===== Some Verilog implementation notes =====
==== module ====
A module in a Verilog netlist is a collection of components similar to a SPICE
subcircuit. Like paramsets, modules are declared at top level only. A module
is an object inherited from BASE_SUBCKT, a COMPONENT that permits subdevices.
Type resolution and elaboration is different to SPICE. Verilog has no
"subtypes": each subdevice refers to a COMPONENT by type name and has
parameters and ports. The type name is resolved after read-in, and held by a
stub device. Prototypes matching the type, port and parameter names of the stub
are collected as candidates in precalc_first.
Once a candidate COMPONENT has undergone precalc_first, "is_valid()" indicates
whether the candidate is the correct one. During expand, a cloned instance of
the stub turns into a singleton subcircuit holding the valid candidate.
This singleton subcircuit is then deflated into just the candidate instance by
the parent module at the end of expand.
According the the Verilog standard, only paramset, a special COMPONENT type
permits overloading. In Gnucap Verilog mode a type name used in a device instance
refers to all the prototypes by that name, regardless of their origin. Possible origins are device plugins and module or paramset statements loaded or parsed at any time before circuit expansion takes place. In spice mode, .subckt and .model commands provide a similar mechanism to create prototypes.
==== paramset ====
A paramset is a COMPONENT with a named reference to another of an underlying
type and a set of parameters, similar to a one-device subcircuit. The type
name is resolved during precalc_first and is supposedly unique. It is currently assumed to be available when parsing the paramset. Port names are inherited from this prototype and available after precalc_first.
The prototype name in a paramset declaration (used in the simulator) resolves to the first component found in its scope. Next, the device_dispatcher is queried. This is where dynamically loaded plugins may provide models. Note that the dispatcher replaces existing entries when installing the same name again, hence the last one loaded wins.
When gnucap-modelgen reads in a paramset, a module definition must be available by the prototype name, i.e. further up within the compilation unit. The standard requires a unique name. If it is not unique, the outcome is undefined (under costruction).
=== example ===
paramset res resistor
parameter R;
.r(R)
endparamset
Paramsets are declared at top level only, and ready before instances are
elaborated when the containing modules are expanded.
The standard allows multiple paramset declarations with the same label (type
name). In Gnucap, type resolution is implemented equally for all COMPONENTS.
During elaboration, paramset instances are flattened. An instance R1, as in
module main();
res #(.R(r0)) R1(a, b);
endmodule
Is transformed into
resistor #(.r(r0)) R1(.p(a), .n(b));
after "paramset res" has been identified as the only valid, matching prototype.
If there are multiple candidates, such as an additional
paramset res resistor
parameter r;
.r(r)
endparamset
===== compiled paramset =====
The motivation for compiled paramsets, as opposed to interpreted ones, or legacy spice .model instances is speed. Compiled paramsets can be an order of magnitude lighter due to the obvious constant pruning and structural collapse. According to the LRM, Section 6.4.2, paramset identifiers need not be unique, and offer
a basis for overloading. We make use of this mechanism in slight deviation from the standard.
In Gnucap, a paramset provides a device prototype, and device prototypes need not be unique. Moreover device prototypes are not distinguishable by their origin. In particular, a paramset name may be identical to the prototype module name. This offers the possibility to bundle multiple optimised paramsets alongside a generic module into a single plugin.
When a compilation unit contains such a module and a set of (supposedly optimised, simplified) specialisations of the same one, modelgen-verilog may bundle them into a single plugin. In this situation the plugin installs a set of device prototypes, and the generic one is installed last. Subsequently declared interpreted paramsets refer to this last installed and generic prototype. This way, interpreted paramsets remain flexible, while compiled paramsets may coexist for speed.
===== Partial specialisation =====
Given a module declaration, the standard does not offer a way to fix a subset of its parameters, while transparently passing through the others. Paramset sort of does it, but not without changing the behaviour of the specialised device relative to the generic one. Typical use cases are devices specialised without noise, without initial conditions, without additional resistors, without temperature, or with their levels fixed (wip...).
===== transition filter =====
The LRM description for the transition function is unclear. The situation where
a new transition overlaps with another needs clarification. A transition filter
holds a waveform, similar to the delay line. It is updated whenever
transition is called.
In any case, the new waveform must be continuous in all arguments of the
new_transition call. Here's how this may be achieved. Suppose, transition is
called at time t0, with delay d, new start time ts=t0+d, rise or fall time rf
and destination l.
If the waveform has a sample at t0, future samples are deleted.
Otherwise, the future samples are deleted, excluding the first one after t0,
say td1. Now let s0 be the slope at t0. The case s0=0 is simple, otherwise assume
s0>0 w.l.o.g.
The sample at td1 has a value v1. This gives rise to a slope ps of (ts, v1),
(ts+rf, l). Also, let vs be the value at ts.
If ps>s0, we drop the sample at td1 and put in (ts,vs). Else if v0