LVS (and also DRC as far as netlist extraction is concerned) provides interfaces to write and read netlists/schematics, annotated layout and LVS results. There are three major categories of I/O:
You can write a netlist file to supply netlists for (functional) simulators for example. Within LVS scripts, the global "target_netlist" statement triggers writing of a netlist (see target_netlist for details).
target_netlist("output.cir", write_spice, "Created by KLayout")
This statement can basically appear anywhere in the LVS script. The netlist will written after the script has executed successfully. The first argument is the file's path (by default relative to the original layout file). The second argument is the "writer". "write_spice" creates a netlist writer writing SPICE format with a limited degree of flexbility. See below for customizing the writer. The third argument finally is an (optional) comment which will be written into the netlist as a header.
The "write_spice" configuration function has two options:
write_spice(use_net_names, with_comments)
Both options are boolean values. If true and present, the first option will make the writer use the real net's names instead of numerical IDs. If true and present, "with_comments" will embed debug comments into the netlist showing instance locations, pin names etc.
Further customization can be achieved by providing an explicit SPICE writer with a delegate (see NetlistSpiceWriterDelegate). For doing so, subclass NetlistSpiceWriterDelegate and reimplement one or several of the methods provided for reimplementation. Those are NetlistSpiceWriterDelegate#write_device, NetlistSpiceWriterDelegate#write_device_intro and NetlistSpiceWriterDelegate#write_header.
Here is an example that supplied subcircuit models rather than device elements:
# Write extracted netlist to extracted.cir using a special # writer delegate # This delegate makes the writer emit subcicuit calls instead of # standard elements for the devices class SubcircuitModels < RBA::NetlistSpiceWriterDelegate def write_header emit_line(".INCLUDE 'models.cir'") end def write_device(device) str = "X" + device.expanded_name device_class = device.device_class device_class.terminal_definitions.each do |td| str += " " + net_to_string(device.net_for_terminal(td.id)) end str += " " + device_class.name str += " PARAMS:" device_class.parameter_definitions.each do |pd| str += " " + pd.name + ("=%.12g" % device.parameter(pd.id)) end emit_line(str) end end # Prepare a writer using the new delegate custom_spice_writer = RBA::NetlistSpiceWriter::new(SubcircuitModels::new) custom_spice_writer.use_net_names= true custom_spice_writer.with_comments = false # The declaration of netlist production using the new custom writer target_netlist("extracted.cir", custom_spice_writer, "Extracted by KLayout")
This script will produce the following netlist for the simple inverter from the LVS introduction. Instead of printing "M" elements - which is the default - subcircuit calls are produced. This allows putting more elaborate models into subcircuits. The device class name addresses these model subcircuits:
* Extracted by KLayout .INCLUDE 'models.cir' .SUBCKT INVERTER X$1 VDD IN OUT NWELL PMOS PARAMS: L=0.25 W=1.5 AS=0.675 AD=0.675 PS=3.9 PD=3.9 X$2 VSS IN OUT SUBSTRATE NMOS PARAMS: L=0.25 W=0.9 AS=0.405 AD=0.405 PS=2.7 + PD=2.7 .ENDS INVERTER
Netlists can be written directly from the netlist object. Within the script, the netlist object can be obtained with the netlist function. This function will first trigger a netlist extraction unless this was done already and return a Netlist object. Use Netlist#write to write this netlist object then. Unlike "target_netlist", this method is executed immediately and this way, a single netlist can be written to multiple files in different flavours.
The main use case for reading netlists is for comparison in LVS. Reference netlists are read with the "schematic" function (see schematic):
schematic("inverter.cir")
Currently SPICE is understood with some limitations: Only a subset of elements is implemented by default. These are "M" (gives "MOS4" device classes), "Q" (gives BJT3 or BJT4 device classes), "R" (gives Resistor device classes), "C" (gives Capacitor device classes) and "D" (gives diode device classes).
As for the SPICE reader, a delegate can be provided to customize the reader. For doing so, subclass the NetlistSpiceReaderDelegate class and reimplement the methods provided. These are: NetlistSpiceReaderDelegate#wants_subcircuit, NetlistSpiceReaderDelegate#element, NetlistSpiceReaderDelegate#finish and NetlistSpiceReaderDelegate#start
This example customizes a reader to pull MOS devices from subcircuit models rather than from "M" elements. Basically this customization does the opposite part of the writer customization before (only for MOS devices).
# Provides a SPICE netlist reader delegate which turns # some subcircuit models (for subcircuits NMOS and PMOS) # into devices class SubcircuitModelsReader < RBA::NetlistSpiceReaderDelegate # implements the delegate interface: # says we want to catch these subcircuits as devices def wants_subcircuit(name) name == "NMOS" || name == "PMOS" end # implements the delegate interface: # take and translate the element def element(circuit, el, name, model, value, nets, params) if el != "X" # all other elements are left to the standard implementation return super end if nets.size != 4 error("Subcircuit #{model} needs four nodes") end # provide a device class cls = circuit.netlist.device_class_by_name(model) if ! cls cls = RBA::DeviceClassMOS4Transistor::new cls.name = model circuit.netlist.add(cls) end # create a device device = circuit.create_device(cls, name) # and configure the device [ "S", "G", "D", "B" ].each_with_index do |t,index| device.connect_terminal(t, nets[index]) end # parameters in the model are given in micrometer units, so # we need to translate the parameter values from SI to um values: device.set_parameter("W", (params["W"] || 0.0) * 1e6) device.set_parameter("L", (params["L"] || 0.0) * 1e6) return true end end # Instantiate a reader using the new delegate reader = RBA::NetlistSpiceReader::new(SubcircuitModelsReader::new) # Import the schematic with this reader schematic("inv_xmodels.cir", reader)
The layout-to-netlist database (L2N DB) is written using the global report_netlist function. This function can be put anywhere in the script. Writing will happen after the script executed successfully:
report_netlist("extracted.l2n")
Without the filename, only the netlist browser will be opened but no file will be written. The layout-to-netlist database is a KLayout-specific format. It contains the netlist information plus the shape and instance information from the layout. L2N databases can be read into the netlist browser for example. Hence exchange of extracted netlists is possible.
The Layout-vs-schematic database (LVS DB) is written using the global report_lvs function. This function can be put anywhere in the script. Writing will happen after the script executed successfully:
report_lvs("extracted.lvsdb")
Without the filename, only the netlist browser will be opened but no file will be written. The LVS database is a KLayout-specific format. It contains the extracted netlist information, the reference netlist and the cross-reference table. LVS databases can be read into the netlist browser for example. Hence exchange of LVS reports is possible.