To define custom element class we should use ::SpiceGenTcl::Device class as a superclass. Why we need this? One simple example is custom Verilog-A device, that could have arbitary number of pins and parameters. Let's try to define new element for next Verilog-A device:
This model is taken from designers-guide, that contains many Verilog-AMS models.
In this block of code we define instance class of core. Let's go through the each line. First line is class definition:
It is good convention to start class name from capital letter.
First argument is name of instance, then positive node and negative node names, args accepts model value after -model key and all instance parameters of device.
In these few lines we process args variable with argparse package:
Here we define required -model argument, and optional instance arguments -len and -area with default values. Due to these lines we can pass parameters to element in forms -paramName $paramValue $paramQual, for example: -area 1e-4 or -area l*m -eq.
Possible qualificator is -eq - it means that parameter should be inserted into SPICE netlist as paramName={expression}
In the next few lines we build parameter list that will be passed to ::SpiceGenTcl::Device constructor:
The last but not the least is step of passing arguments to ::SpiceGenTcl::Device constructor:
This superclass accept arguments in form constructor {name pins modelName instParams}. So, we pass string n$name as device name, n is reference designator for Verilog-a devices in Ngspice. Secondly we pass list of nodes [list "p $pNode" "n $nNode"] in form of {{Name0 NodeName} {Name1 NodeName} {Name2 NodeName} ...}. And the last is list of parameters in form {{model Value -posnocheck} {Name Value ?-eq?} {Name Value ?-eq?} {Name Value ?-eq?} ...}.
So, after that we can create a new instance of newly created class:
Resulted SPICE string is:
Verilog-A devices in Ngspice demand corresponding model being instantiated in the netlist. For that purpose we create CoreModel class:
Here we use as superclass ::SpiceGenTcl::Model for new element. Next we define constructor for model, first argument is name of model and args that accepts model value all instance parameters of device. To process optional arguments, we use special method ::SpiceGenTcl::KeyArgsBuilder::argsPreprocess, that process optional args arguments and produces string for ::SpiceGenTcl::Model constructor.
We set model parameters in list paramsNames, and after that we pass arguments to superclass's constructor. The name coreja corresponds to name of Verilog-A module.
We use ::SpiceGenTcl::KeyArgsBuilder::argsPreprocess procedure here, but we can go the same way we do with Core class, and add, for example, default values, or use some complex logic of elements initialization:
Finally, we initialize model instance and print SPICE string:
The result is:
If you want to see the simulation example with that model, please look at /examples/ngspice/advanced/verilog_a_magnetic.tcl.