Introduction
Open source tools, and complete design flows, for the design of digital ASICs were readily available for some time, and what was lacking was foundry support. Process information and Process Design Kits (PDKs) were behind NDA wall, limiting availability and virtually eliminating the possibility of open chip designs. Furthermore, PDKs were designed for proprietary EDA tools that are prohibitively expensive and subject to restricted availability.
That has changed when some foundries, such as Skywater, Global Foundries and IHP Microelectronics, have released open source PDKs. Open source PDK discloses the details of a process, such as layout design rules and device models, that enable the design and layout of manufacturable custom analog and digital circuits.
Availability of open source tools and PDKs has spurred open chip design and innovation. However, having access to device models and layout design rules is not enough for building a complex SoC, as it requires the use of many IPs. Open source community has developed many digital IPs, and the development has accelerated by the use of higher level hardware design languages. Power, performance and area (PPA) are metrics of digital IPs that not only depend on the quality of RTL code, but also on the standard cell library used for implementation.
There isn’t a single metric for quality of standard cell library, as the library can be designed for minimum area, low leakage, high speed etc. In principle, any digital circuit can be implemented with only a NAND (or a NOR) gate and a storage element (flip-flop), but that would result in a suboptimal design. Better PPA can be achieved if the standard cell library has more logic gate types in various drive strengths. Therefore, it is desirable for a digital standard cell library to have as many logic gates and storage elements in different drive strengths as possible.
The following sections cover the role and uses of a digital standard cell library in a digital design flow. A short overview of digital design flow summarizes the steps and tools used in the design of digital SoC. The section on design of a standard cell goes into details of all aspects of cell design. Design flow section covers the details of generating the standard cell library from individual cell netlists and layouts.
A short overview of ASIC digital design flow
The objective of this section is to give a short overview of ASIC digital design flow and the role, uses and different libraries and formats that make a digital standard cell library.
The design of a digital chip is traditionally partitioned into two major phases: front-end and back-end design.
“Front-end design” refers to the design of a digital chip on a logical level in a Hardware Description Language (HDL), such as VHDL, SystemVerilog, Chisel, SpinalHDL, …, or in a High Level Synthesis (HLS) language, or any combination of them. Design in HDLs is usually called Register-Transfer Level (RTL), because data is explicitly moved between registers, while the HLS design is algorithmic oriented, without explicit register assignments or register instantiation. Eventually the HLS design is compiled and translated (lowered) to (usually) SystemVerilog that is used in the backend phase. We will assume that the RTL design is done and focus only to back-end design.
“Back-end design” refers to the physical implementation of the designed logic in a chosen integrated circuit process. Physical implementation takes many steps, and in the end results in geometric representation of transistors, vias, metal traces etc. that is used to produce mask layouts for chip fabrication. Geometric representation of chip layout is usually stored in an industry standard library format such as GDSII, OASIS or OpenAcess.
GDSII is the oldest - created in 1970s - and the simplest format, that is still used. It stores per-layer geometry as paths, rectangles, polygons, labels and other shapes grouped in cells, that can be referenced in other cells to form complex layouts.
OASIS (Open Artwork System Interchange Standard) is a standard from 2002 that was designed to be a replacement for GDSII. It is an industry standard, but it is not open.
OpenAccess is (contrary to its name) a proprietary set of standards used in proprietary tools to allow interoperability. Although original motivation was to ensure interoperability, many vendors have added extensions that break it, and is usually not used in open source tools.
Geometry stored in a layout library is used to produce a set of mask layouts, usually by performing Boolean operations on shapes in different layers. Each mask layout is used to produce a reticle used in one or more steps during the fabrication of chip features, and defines a certain feature - such as opening for source or drain diffusion implants, polysilicon gates, contacts, vias, metal traces etc., and ultimately define the functionality of the chip on the lowest level.
Back-end design can be done by manually drawing transistors, either as parametrized cells or rectangles of polysilicon over rectangles of diffusion, contacts and interconnects in metal layers. This kind of design is called “full custom design” and is rarely done in practice because it is very time consuming and infeasible for complex designs. Full custom design is usually only used for timing/power critical parts, such as design of bit slice in Arithmetic-Logic Unit.
Back-end implementation can be simplified and automated by using a library of pre-defined logic cells. Pre-defined cells should contain transistors and local interconnect that implement a certain logic function. All information about semiconductors (transistors) is contained in a standard cell, so only the choice of cells, their placement and metal routing is done in the back-end phase. Such a library of pre-defined cells is called a standard digital cell library, and is technology dependent.
It may seem that there is a clear-cut divide between front-end RTL design to solve a logical problem, and a back-end design to implement it, but it is not the case. Delay of standard cells affects the optimal pipelining strategy for a given operating frequency - the same design may need fewer pipeline stages to work at the same operating frequency in a more advanced process node, and that should be accounted in the RTL design. Tools for digital design flow may reduce the impact of suboptimal choice of pipelining strategy by register retiming but it will nevertheless be a suboptimal design.
Even in the same process node, different metal stack options can result in routing congestions that may determine the optimum bus width for a specified operating power, performance and area (PPA). Again, digital design tools may soften be performance degradation, but the design will not be optimal.
One of the most advanced open source flows for back-end RTL to GDSII digital design is OpenROAD. OpenROAD is a collection of tools and scripts for back-end digital implementation, that is shown in the figure below (taken from OpenROAD README).
%%{init: { 'logLevel': 'debug', 'theme': 'dark'
} }%%
timeline
title RTL-GDSII Using OpenROAD-flow-scripts
Synthesis
: Inputs [RTL, SDC, .lib, .lef]
: Logic Synthesis (Yosys)
: Output files [Netlist, SDC]
Floorplan
: Floorplan Initialization
: IO placement (random)
: Timing-driven mixed-size placement
: Macro placement
: Tapcell and welltie insertion
: PDN generation
Placement
: Global placement without placed IOs
: IO placement (optimized)
: Global placement with placed IOs
: Resizing and buffering
: Detailed placement
CTS : Clock Tree Synthesis
: Timing optimization
: Filler cell insertion
Routing
: Global Routing
: Detailed Routing
Finishing
: Metal Fill insertion
: Signoff timing report
: Generate GDSII (KLayout)
: DRC/LVS check (KLayout)
The first step in a digital implementation flow is (logic) Synthesis. Synthesis is a process that takes RTL description of a digital design, SDC (Synopsys Design Constraints) file for design constraints and Liberty library of standard cells. SDC file is written by a designer to define design constraints such as input/output delays, clocks, timing exceptions etc. Liberty library contains information about gates available in a standard cell library: input and output pins, logic function, tables for delay and power calculation.
Synthesis tool, in this case Yosys, reads in the RTL design, parses it and transforms it into a set of Boolean expressions and memory elements (flip-flops or latches) for implementation of combinatorial and sequential circuits.
The main task of a synthesis tool is to map the design’s Boolean equations to available logic cells present in the Liberty library. In theory, any Boolean expression can be mapped to a complete set of Boolean functions. A complete set of Boolean functions can be a single function, e.g. NAND forms a complete set, and so does NOR. However, synthesizing a digital circuit by using only NAND or NOR gates, although theoretically correct, is beyond suboptimal. For example, it takes 4 NAND gates to implement a NOR gate, and vice-versa.
A specialized, carefully designed and optimized set of cells that implement various digital functions with several inputs, and possibly with variants of some inputs/outputs inverted, enables the synthesis tool to optimally map the design equations to available cells. Having in mind that logic mapping is a NP-hard problem, and that synthesis tools use heuristics, and possibly non-deterministic algorithms, to solve a problem in a reasonable time, optimality should be understood conditionally.
The output of a synthesis tool is a structural, i.e. gate level, Verilog netlist that implements the user’s design. Structural Verilog netlist instantiates library cells and connects their inputs/outputs - it does not contain any behavioral code. Therefore, it can be implemented in silicon by placing the instantiated cells and connecting them with metal tracks.
Floorplanning tool reads a structural Verilog netlist and a LEF (Library Exchange File) library that contains abstract views of standard cells and places them in a grid. Abstract view is a simplified layout of the cell, containing only the data pertinent to digital implementation - cell shape, pin positions and shape, routing obstructions and gate or diffusion area that is used in antenna violation checks. Floorplanning tool also places special cells if they are needed, such as substrate and n-well ties for biasing, or dedicated end cap cells on edges of the placement grid.
Placement tool then places the cells so that connected cells are close to optimize routing. Besides placing the cells present in the structural Verilog netlist, placement tool may insert buffers to reduce the fan out of heavily loaded outputs, and it may resize the existing cells.
Standard cells are made in several “sizes”, where the cell “size” refers to the drive strength of a cell.
Drive strength of a cell is usually denoted as a suffix _Dx, where x is the drive strength x=1,2,....
Cells with higher drive strength have wider transistors and can drive higher capacitive loads for the same propagation time, but they occupy more area, and have a higher static and dynamic power.
To minimize power consumption and occupied silicon area, a cell with minimum drive strength should be used, provided that it can drive a given output load according to timing constraints.
Therefore, it is desirable to have a cell in many drive strengths so that a tool can perform cell resizing effectively.
Clock signal is used to synchronize data transfers between sequential elements, such as flip-flops, and is a very high fanout net. Distributing the clock signal to every flip-flop with low timing skew is not trivial, and is in a Clock Tree Synthesis (CTS) step. In a CTS step a clock tree, consisting of dedicated clock buffer cells, is designed to minimize the timing skew between different clock leaf outputs.
In the routing phase the inputs and outputs of cells are connected with metal tracks and vias. Minimum spacing between metal tracks and the height of standard cells determine the number of horizontal tracks available per row of standard cells, while the number of vertical tracks is determined by the width of the digital design. Horizontal and vertical tracks are usually assigned to alternate metal layers, e.g. horizontal tracks in even metal number (M2, M4, etc.) and vertical tracks in odd metal number (M3, M5, etc.), while lowest metal level (M1) is usually used for local routing in standard cells. Assignment of metals to horizontal and vertical tracks is not strict - a short vertical segment can be made in metal assigned to horizontal tracks to minimize the number of vias.
Routing congestion occurs if there are not enough routing resources, i.e. horizontal and vertical tracks, to route all nets and the design cannot be routed at all, or is routed with a severe timing penalty. It can be resolved by increasing the digital core area, or equivalently reducing the core utilization ratio, and re-run the place and route. Core utilization ratio is the ratio of the area of gates to the core area. Typical core utilization is in the range of 80%, but can be less for designs with many high fanout nets, or higher for designs with low fanout.
Finalization step inserts metal fills - patches of dummy metal patterns - that bring the local and global metal density into range required by the foundry DRC rules. Following the minimum and maximum density rules, both of metals and diffusion and polysilicon, is important to achieve the required planarity during manufacturing. Dummy fill is followed by parasitic RC extraction and final static timing checks. Finally, GDSII layout is generated by replacing the abstract LEF views with cell layouts.
Digital implementation flow essentially only places standard cells and routes metal tracks - no semiconductor design is involved at all, as the semiconductor part is contained in the design of standard cells.
To summarize, the effectiveness of the digital design flow is strongly dependent on the quality of a standard cell library used. Standard cell library with a large number of cells implementing various Boolean functions in many drive strengths is likely to produce better PPA than a library with fewer cells.
Standard cell design
Standard digital cell library provides logical and physical views that are used in digital design flow to implement the chip functionality. Ideally, standard cells should have minimum delay and power - both static and dynamic, while minimizing area and substrate noise injection. It goes without saying that it is not possible to minimize delay/power/area/noise simultaneously, so a compromise must be made to optimize chosen metrics at the expense of others.
There are two types of standard digital cell libraries: pad ring and core.
- Pad ring cells are connected to chip bonding pads and are arranged into a continuous ring around chip core - hence the name pad ring cells. These cells are interface between chip core logic that operates at low voltage (e.g. 1.2 V for 130 nm CMOS) and I/O voltage (e.g. 1.8 or 3.3 V). They also provide electrostatic discharge (ESD) protection of inputs, outputs and power pins.
- Core cells are used for implementing digital logic in the chip core, and they are usually referred to standard digital cells.
Different designs have different requirements, and there isn’t a “one size fits all” standard cell digital library. To address this issue, some foundries provide several variants (flavours) of standard digital cell libraries that are optimized for different criteria:
- General purpose (GP). GP cells are designed as a compromise of area/delay/power/noise performance, and are suitable for general purpose designs.
- High density (HD). HD cells are optimized for minimum area at expense of delay/power/noise performance. These cells usually don’t have n-well/substrate ties to reduce the area, and require the use of special cells for biasing. Few n-well/substrate ties results in higher substrate noise injection, that could be a problem in mixed-signal designs.
- High performance (HP). Cells are optimized for minimum delay at expense of area/power/noise performance. Area is increased because high performance cells usually contain n-well and substrate ties to pick up charge injected into substrate due to high slew rate and current switching. HP cells can use transistors with lower threshold voltage (low-Vt) to increase the current drive and reduce gate propagation delay. Dynamic power is increased due to higher drive current and operating frequency, but static power is increased as well due to increased leakage of low-Vt transistors.
- Low noise (LN). Cells are optimized to minimize substrate noise injection. Low noise cells contain n-well and substrate ties connected to a separate rails to minimize substrate noise injection.
- Junction isolated (JI). Junction isolated cells use triple wells to isolate the digital circuits from substrate. JI cells minimize the substrate noise injection.
- Low leakage (LL). Low leakage cells use hight threshold voltage (high-Vt) transistors to minimize leakage current of transistors in off state, and consequently static power.
Regardless of flavour of standard cell library, it consists of several standard cell types:
- Core logic. These cells implement various Boolean functions and memory elements (flip-flops and latches) in variety of drive strengths. Constant ‘1’, called “tie high”, and constant ‘0’, called “tie low”, cells should be included to avoid connecting transistor gates directly to power and ground.
- Scan chain cells. These cells are used to insert a Design for Test (DFT) scan chain during synthesis. Scan chains are used in automated factory testing to screen for defect chips early.
- Physical cells. These cells do not implement logic, but are needed for physical implementation. Standard cells that do not have integrated n-well and substrate ties require regular placement of special cells that tie n-well to power supply and substrate to ground. Core utilization is never 100%, and filler cells need to be inserted in unplaced sites to connect power and ground rails. Besides connecting the power and ground rails, filler cells also ensure that minimum density of polysilicon and diffusion is met. Alternatively, a cell with capacitor between power and ground can be inserted to provide local power decoupling. Care should be taken when inserting decoupling cells as they are implemented as MOS capacitors that can increase static power due to gate oxide leakage. Some standard cells require placement of special cells at the edges and corners of cell array to satisfy design rules, that are called “end cap” and “corner cap” cells. Finally, “antenna cells” are used to fix antenna DRC errors that occur for large ratio of metal to gate area.
Standard cells should be compact, routed with minimum number of layers - usually only in diffusion, polysilicon and metal 1, so that maximum number metals can be used for routing. Cells in advanced nodes might use more than one metal layer to reduce the cell area, but this is offset by large number of available metal layers.
Digital implementation floor planner tool places standard cells in a regular array, as shown in the figure below. Array rows are separated by alternating horizontal power (VDD) and ground (GND) rails. Standard cells are Y mirrored in alternating rows to properly connect the power and ground, as is indicated by the orientation mark.
Router uses pre-defined set of metal track widths and vias (also called “cuts” in a digital flow) for routing. Information about cell geometry and routing is contained in a technology LEF file, that is an integral part of a standard digital library.
Anatomy of a standard cell
For a cell library to be usable in an automated digital implementation flow, it must conform to some rules:
- Cells must have the same height so that they can be placed in rows.
- Cell width must be an integer multiple of unit cell width.
- Cells must have power and ground rails that can be connected by abutment.
- Cells must conform to DRC rules, even when abutted to any other cell in any valid X/Y mirror condition.
- Cell pins must be on a routing grid and accessible by via without DRC violations.
A standard cell site template shown in the figure below can solve most of the standard cell requirements.
Standard cell site template provides:
- Place and Route (PnR) boundary for cell alignment and abutment. Cell layout extends beyond PnR boundary, and when cells are abutted from left, right, top and bottom some parts of layouts overlap. For example, abutting a cell from top or bottom results in overlap of power rails, diffusions and contacts, but the composite layout is still DRC clean.
- Power and ground rails in metal 1.
- N-well for PMOS transistors. Height of n-well is chosen to reflect the different widths of NMOS and PMOS transistors due to different hole and electron mobilities.
- N-well and substrate ties, that include diffusion, P and N implants and contacts to metal 1. Contacts to diffusions are arranged on a grid so they are aligned with cells from rows above and below.
- Height of all cells is guaranteed to be the same, and cell width is always an integer multiple of a unit cell width. Cells have half of unit width on left and right sides to allow seamless abutment of standard cells.
- Routing grid markings, shown in red dashed lines, to explicitly show routing tracks and possible pin placements.
- General keep out areas on the left and right sides of a cell are sized so that minimum spacing rules are satisfied in all cases.
When cells are abutted, keep out areas of adjoining cells are joined, so the width of two keep out areas should be equal to largest minimum spacing rule of layers used in a standard cell. Layout of standard cells uses only diffusion, polysilicon and metal 1, so the width of keep out area is
keepout_w = 0.5 * max(min_spacing(diffusion), min_spacing(polysilicon), min_spacing(metal_1))
In the case of IHP SG13G2 process design rules minimum dimensions areLayer Minimum spacing Diffusion 210 nm Polysilicon 180 nm Metal 1 180 nm Keep out 105 nm - Channel keep out area is determined by DRC rule that sets minimum distance of NMOS transistor channel to n-well and minimum n-well enclosure of PMOS transistor channel. For IHP SG13G2 process design rules minimum channel keep out distance from n-well edge is determined by rules NW.c and NW.d, and is 310 nm, for a total of 620 nm channel keep out area height.
Standard cell site template can speed up the development of layouts as physical constraints are marked, as well as pin positions. The only thing that remains is to define the unit site size. Unit site height is traditionally “measured” in the number of horizontal metal tracks that can be routed over the height of a cell - usually 7 metal tracks for high density cell, and 9 or 11 tracks for general purpose and high performance cells, while the width is determined by vertical metal track pitch.
Metal track pitch is constrained by minimum metal width and spacing, although other factors such as design for manufacturability (DFM) or better track utilization may affect the decision on track pitch. On a first glance, horizontal and vertical track pitch should be the same because it is common that metals used in digital implementation have the same minimum width and spacing. Metal 1 is an exception and usually has smaller minimum width and spacing to allow more compact routing in standard cells.
In SG13G2 process minimum metal 2 to 5 width is 200 nm and minimum spacing is 210 nm. This results in theoretical minimum for track pitch of 410 nm. Open source IHP standard cell library uses 420 nm pitch for horizontal metals (M2 and M4) and 480 nm for vertical metals (M3 and M5). Horizontal pitch of 420 nm, instead of minimum of 410 nm, can be attributed to relaxed spacing to increase the yield, but it does not explain why the vertical track pitch is 480 nm. The reason for larger vertical track pitch is that via connected to metal has to have an endcap overhang (rule Mn.c1 requires 50 nm) that enlarges the metal width. For a single via, that is usually used for routing digital designs, endcap overhang must be present on at least two sides, resulting in minimum track pitch of 200 + 210 + 50 = 460 nm, that is close to 480 nm used in IHP cells. Such arrangement reserves space for endcaps only in one dimension, that might result in routing obstruction in dense designs.
GLOW SG13G2 standard cell library is designed to use the same routing pitch for both horizontal and vertical tracks of 480 nm, reserving space for endcaps in both dimensions. Increasing the horizontal pitch from 420 to 480 nm results in 9 tracks cell height of 4.32 um instead of 3.78 um - about 15% increase in unit cell area. This area is not wasted for several reasons (numbers for DRC rules are for IHP SG13G2 technology):
- Minimum spacing of well/substrate tie to transistor channel is 300 nm. Together with 150 nm of tap diffusion inside the PnR area results in minium of 450 nm spacing of transistor channel from PnR vertical boundary. Including 620 nm of channel keep out area near n-well edge, it results in 1520 nm of cell height that cannot be used for transistor gates. Nine track cell with horizontal pitch of 420 nm has a total height of 3.78 um, but transistor channels can only occupy height of 2.26 um, while the cell with 480 nm pitch has a total height of 4.32 um, where transistors can occupy height of 2.8 um - an increase of almost 24%. This increase should be understood as a potential for area reduction as 15% of cell area is traded for (potential) 24% increase in transistor area. Not all cells will benefit from space available for wider transistors, but many cells will, especially if transistor dimensions are chosen to maximize the use of available area.
- More space for routing of complex logic inside a standard cell, potentially reducing cell width and overall area.
- Flexibility of via endcaps in both horizontal and vertical tracks might reduce routing obstructions.
Design flow for standard digital cells
Netlist design
Layout design and verification
Cell characterization
Abstract view generation
Standard cell library assembly
Design flow
GLOW Utilities
Python package glow_utils provides utility functions used for design of digital standard cell library.
Package can be installed with
pip install .
For development it is convenient to link the Python files to the package installation as
pip install -e .
so that changes in Python files are immediately available without the need to reinstall the package.
Quick start
GLOW utilities are designed to aid the development of circuits, specifically digital standard cells. They provide a programmatic way for development of parametrized hierarchical circuits, performing various checks, transformations and exporting to SPICE or CDL netlists.
Minium example of parametrized CMOS inverter is
from glow_utils.symsubcircuit import Symsubcircuit
from glow_utils.symmosfet import SymNMOS, SymPMOS
inv_par = Symsubcircuit("inv_par", ['A', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'WP' : 450e-9, 'L' : 130e-9, 'NGN' : 1, 'NGP' : 1})
n = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : 'ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
p = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
inv_par.addElement([n, p])
print(inv_par.netlist_SPICE())
The output of this Python code is
.subckt inv_par A Y VDD VSS WN=3e-07 WP=4.5e-07 L=1.3e-07 NGN=1 NGP=1
MN0 Y A VSS VSS sg13g2_lvnmos w={WN} l={L} ad={WN*3.1e-07} as={WN*3.1e-07} pd={2*(WN+3.1e-07)} ps={2*(WN+3.1e-07)} ng={NGN}
MP0 Y A VDD VDD sg13g2_lvpmos w={WP} l={L} ad={WP*3.1e-07} as={WP*3.1e-07} pd={2*(WP+3.1e-07)} ps={2*(WP+3.1e-07)} ng={NGP}
.ends
A simple CMOS inverter testbench can be made by creating an instance of the inverter and defining transistor models - in this case a dummy LEVEL 1 MOSFET model.
X1 in out vdd 0 inv_par
.model sg13g2_lvnmos NMOS (LEVEL=1 VTO=0.3 KP=50u)
.model sg13g2_lvpmos PMOS (LEVEL=1 VTO=-0.3 KP=20u)
What is left is to define voltage sources and simulations
Vdd vdd 0 1.2
Vin in 0 PULSE(0 1.2 0 1n 1n 10n 20n)
Cl out 0 10f
.tran 0.1n 100n
.control
run
plot v(in) v(out)
.endc
.end
Saving the file as tb_inv.cir and running a simulation with
ngspice tb_inv.cir
results in an error
unknown parameter (ng)
Simulation interrupted due to error!
This error is a consequence of a chosen inadequate MOSFET model that does not support parameter ng, that is supported by target technology. Netlist can be corrected by deleting the extra parameter, and the whole corrected netlist is given in the listing below:
* CMOS Inverter Testbench
.subckt inv_par A Y VDD VSS WN=3e-07 WP=4.5e-07 L=1.3e-07
MN0 Y A VSS VSS sg13g2_lvnmos w={WN} l={L} ad={WN*3.1e-07} as={WN*3.1e-07} pd={2*(WN+3.1e-07)} ps={2*(WN+3.1e-07)}
MP0 Y A VDD VDD sg13g2_lvpmos w={WP} l={L} ad={WP*3.1e-07} as={WP*3.1e-07} pd={2*(WP+3.1e-07)} ps={2*(WP+3.1e-07)}
.ends
X1 in out vdd 0 inv_par
.model sg13g2_lvnmos NMOS (LEVEL=1 VTO=0.3 KP=50u)
.model sg13g2_lvpmos PMOS (LEVEL=1 VTO=-0.3 KP=20u)
Vdd vdd 0 1.2
Vin in 0 PULSE(0 1.2 0 1n 1n 10n 20n)
Cl out 0 10f
.tran 0.1n 100n
.control
run
plot v(in) v(out)
.endc
.end
This time the simulation runs successfully and the input and output waveforms are displayed.
This simple example shows how to use GLOW utils to design a CMOS inverter, but they can be used for much complex cases.
Symtech class
Symtech class is intended to be a container of all relevant technology information, so that the rest of the glow_utils classes can be used with any technology.
Technology information is stored in a dictionary, that can be read from JSON file.
For example, JSON file for IHP SG13G2 technology is:
{
"processName" : "sg13g2",
"nmosModelName" : "sg13g2_lvnmos",
"pmosModelName" : "sg13g2_lvpmos",
"nmosAS" : "ipar('w') * 310e-9",
"nmosAD" : "ipar('w') * 310e-9",
"nmosPS" : "2 * (ipar('w') + 310e-9)",
"nmosPD" : "2 * (ipar('w') + 310e-9)",
"pmosAS" : "ipar('w') * 310e-9",
"pmosAD" : "ipar('w') * 310e-9",
"pmosPS" : "2 * (ipar('w') + 310e-9)",
"pmosPD" : "2 * (ipar('w') + 310e-9)"
}
Predefined key names are given in the following table.
| Key | Description |
|---|---|
| processName | Name of technology. |
| nmosModelName | Name of NMOS transistor model. |
| pmosModelName | Name of PMOS transistor model. |
| nmosAS | Expression to calculate NMOS source diffusion area. |
| nmosAD | Expression to calculate NMOS drain diffusion area. |
| pmosAS | Expression to calculate PMOS source diffusion area. |
| pmosAD | Expression to calculate PMOS drain diffusion area. |
| nmosPS | Expression to calculate NMOS source diffusion perimeter. |
| nmosPD | Expression to calculate NMOS drain diffusion perimeter. |
| pmosPS | Expression to calculate PMOS source diffusion perimeter. |
| pmosPD | Expression to calculate PMOS drain diffusion perimeter. |
Function ipar used in expressions is defined in the Symdevice class.
New keys can be added to JSON file and used to expand or add new functionalities. Unused keys in JSON file will be ignored.
Symdict class
Symdict is a sub-class of Python dict to allow hierarchical parameter evaluation.
It is mainly used internally in other classes to build hierarchy of circuit parameters.
Dictionary keys and values are evaluated in a hierarchical manner,
where keys and values defined in local dictionary take precedence over upper levels.
For example:
from glow_utils.symdict import Symdict
topDict = { 'x':1, 'y':2 }
level1Dict = Symdict( topDict, localDict={'x':5 } )
print(level1Dict['x'])
# 5
level1Dict['x'] evaluates to 5 instead of 1, since the local parameter value of x takes precedence over the global value.
The same principle applies for any depth of dictionary hierarchy.
If the key in not defined in any dictionary, a KeyError exception is raised, as in a regular Python dict.
Symparam class
Symparam class is used to evaluate symbolic parameter expressions.
Symbolic expressions can be expanded by substitution so that resulting expression contains only symbols that evaluate to numbers or are not defined. Alternatively, symbolic expressions can be evaluated to get a numeric value.
When Symparam is used with Symdict, it provides means for hierarchical evaluation of expressions, either to symbolic expression or numerical value, and is mainly used internally by other classes.
Symparam constructor takes two arguments, a dictionary of parameters paramDict and a dictionary of functions fnDict:
evaluator = Symparam(paramDict, fnDict)
Instance evaluator of Symparam uses provided paramDict and fnDict to substitute or evaluate symbolic expressions.
Method substitute performs symbolic substitution on a given expression and results in a symbolic expression.
def substitute(self, paramExpr, instanceFns = {}, allowSymbols = False):
Argument paramExpr is a string containing an expression to work on.
Optional argument instanceFns allows for additional, e.g. per-instance defined, functions that are not defined in fnDict.
Optional argument allowSymbols controls the behavior of symbolic substitution. If allowSymbols = False an exception is raised if a symbol is not defined - it does not expand to other symbols nor does it have a numerical value. If allowSymbols = True the substitution stops when a symbol is not defined, without raising an exception.
For example, substitution on expression x + y can be performed as:
evaluator.substitute('x + y') # Result is a symbolic expression
Method evaluate performs symbolic substitution and evaluates a given expression to a number.
def evaluate(self, paramExpr, instanceFns = {}):
Argument paramExpr is a string containing an expression to work on.
Optional argument instanceFns allows for additional, e.g. per-instance defined, functions that are not defined in fnDict.
Method evaluate tries to evaluate an expression to a number, and raises an exception if it can’t be done - e.g. a symbol does not evaluate to number for available parameter and function dictionaries.
For example, evaluation of expression x + y can be performed as:
evaluator.evaluate('x + y') # Result is a number
A complete example of Symparam basic use is given in the code below:
from glow_utils.symparam import Symparam
values = { 'x' : 'a+b',
'y' : 'c',
'a' : 1,
'b' : 2,
'c' : 3}
params = Symparam(values, {})
expression = 'x * y'
print('Dictionary')
print(values)
print('Expression')
print(expression)
print('Expression after substitution')
print(params.substitute( expression ))
print('Expression numeric value')
print(params.evaluate( expression ))
The output of the previous Python code is
Dictionary
{'x': 'a+b', 'y': 'c', 'a': 1, 'b': 2, 'c': 3}
Expression
x * y
Expression after substitution
(a+b)*c
Expression numeric value
9
Symdevice class
Symdevice is base class for devices, and it provides common functions that device should implement.
It should be used as a base class when implementing device models.
Excerpt from Symdevice code shows the minimum device constructor:
class Symdevice(object):
"""
Circuit element base class
"""
deviceType = "unspecified"
modelName = "symdevice"
modelPrefix = "unspecified"
terminals = []
def __init__(self, name, nodes, parameters):
self.name = name
self.nodes = nodes
self.parameters = parameters
self.parameterEvaluator = None # Symparam instance
self.functions = {"ipar" : self.ipar}
self.initInstance()
Class variables deviceType, modelName, modelPrefix and terminals are common for all instances of a given device class.
Class variable deviceType is a string indicating a device type - currently only "nmos" and "pmos" devices are implemented.
Class variable modelName contains a name of SPICE device model that is used as a model in SPICE and CDL netlists.
Class variable modelPrefix is a string that is used as a name prefix.
Class variable terminals is a list of device terminal names. The order of terminals in the list should match the order of terminals in SPICE models.
Device instance is created by making an instance of device class:
dev_inst = dev_cls("inst_name", ['net1', 'net2', 'net3', 'net4'], {'w' : 400e-9, 'l' : 130e-9})
Instance dev_inst is given a name inst_name, and its terminals are connected to nodes ['net1', 'net2', 'net3', 'net4'], and assigned parameter values 'w' = 400e-9 and 'l' = 130e-9. Assigned parameter instance values override the default device parameters.
SymMOSFET class
SymMOSFET is a base class for creating MOSFET devices.
It defines terminal names, their order and SPICE/CDL output formatting, and is used to construct NMOS and PMOS device classes.
MOSFET terminals are assinged as ['D', 'G', 'S', 'B']
SymMOSFET defines the following device parameters:
| Parameter | Description |
|---|---|
| m | Device multiplier. |
| w | MOSFET channel width. |
| l | MOSFET channel length. |
| ad | Drain diffusion area. |
| as | Source diffusion area. |
| pd | Drain diffusion perimeter. |
| ps | Source diffusion perimeter. |
| nrd | Drain diffusion equivalent number of squares. |
| nrs | Source diffusion equivalent number of squares. |
| ng | Number of gates (fingers). |
These parameters are common to many MOSFET models, and SymMOSFET can be used without modification with them.
SymNMOS and SymPMOS
SymNMOS and SymPMOS extend the SymMOSFET class by assigning the device type, model name and model prefix.
They can be used to construct CMOS circuits with parametrized values.
Value parametrization is useful in at least two ways: it provides a way of using standard building blocks to construct complex circuits, and also to capture the design intent by explicitly stating transistor ratios.
NMOS and PMOS devices can be instantiated as
n = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : 'WN', 'l' : 'L'})
p = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'WP', 'l' : 'L'})
In this example the NMOS and PMOS device have been instantiated with symbolic values for channel width and length. Parameters ad, as, ps and pd have the default values, that are taken from technology parameters retrieved from Symtech.
For example, default value for parameter ad is an expression
"ipar('w') * 310e-9"
Function ipar('w') evaluates to the value of instance parameter w, that is 'WN' in the previous example. This symbol can be further expanded by substitution and eventually evaluated at the circuit top level.
Symsubcircuit class
Symsubcircuit is a meta-class used for creating subcircuit classes. Instances of Symsubcircuit class are not objects, but new classes that represent a specific subcircuit which can be instantiated as objects.
For example, an inverter subcircuit class named inv_par with terminals
A, Y, VDD and VSS, and default parameter values WN = 200e-9, WP = 400e-9 and L = 130e-9 can be created as:
inv_par_cls = subcircuit( 'inv_par', ('A', 'Y', 'VDD', 'VSS'), {'WN':200e-9, 'WP':400e-9, 'L':130e-9} )
The newly created subcircuit class, stored in inv_par_cls, can be populated with circuit elements or other subcircuits.
Continuing the inverter example, transistors can be added to subcircuit as:
n0 = SymNMOS('N0', ['Y', 'A', 'VSS', 'VSS'], {'w':'WN'}, {'l':'L'})
p0 = SymNMOS('P0', ['Y', 'A', 'VDD', 'VDD'], {'w':'WP'}, {'l':'L'})
inv_par_cls.addElement( [n0, p0] )
addElement is a classmethod, so the elements are stored as class variables.
This way, the subcircuit elements are shared amongst all instances of the same subcircuit.
At this point the created subcircuit class has no instances.
In order to use the subcircuit, it needs to be instantiated:
inv_par_inst = inv_par_cls('instanceName', ('in', 'out', 'VDD', 'VSS'), {'WN':300e-9, 'WP':600e-9})
By creating a subcircuit instance, it is given a name, connected to given nodes and optionally default parameters are overridden. The subcircuit instance can then be added to a other subcircuit to form a hierarchy:
subckt_cls.addElement( inv_par_inst )
Function ipar can be used to fetch the value of instance parameter,
and ipar('parameter_name') evaluates to the value of instance parameter 'parameter_name'.
For example, NMOS instance with parameters
{'w': 'WN', 'l': 'L',
'as': "ipar('w')*310e-9", 'ad': "ipar('w')*310e-9",
'ps': "2*(ipar('w')+310e-9)", 'pd': "2*(ipar('w')+310e-9)"}
uses ipar function in expressions for as, ad, ps and pd.
In this example, ipar('w') evaluates to the value of instance parameter 'w', so ipar('w') = 'WN'.
Symbolic value 'WN' can further be evaluated to other expressions or a number, depending on the upper level subcircuit parameters.
Function ppar can be used to fetch the value of instance parent, which is usually a subcircuit.
For example, a CMOS inverter
inv_par = Symsubcircuit("inv_par", ['A', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'WP' : 450e-9, 'L' : 130e-9, 'NGN' : 1, 'NGP' : 1})
n = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : 'ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
p = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
inv_par.addElement([n, p])
uses ppar to evaluate the value of 'WN', 'L' and 'NGN' for a given subcircuit instance.
Continuing the example with two instances of inv_par that have different values of parameters:
inv1 = inv_par("inv1", ['A', 'net1', 'VDD', 'VSS'], {'WN' : '1e-6', 'WP' : '2e-6', 'NGN' : 2, 'NGP' : 2, 'L' : 130e-9})
inv2 = inv_par("inv2", ['net1', 'Y', 'VDD', 'VSS'], {'WN' : '1.5e-6', 'WP' : '3e-6', 'NGN' : 4, 'NGP' : 4, 'L' : 130e-9})
we have that ppar evaluates to different values in different instances:
inv1: ppar("WN") = 1e-6
inv2: ppar("WN") = 2e-6
Use of ppar enables creation of parametrized hierarchical circuits.
Dictionary of all defined subcircuits can be obtained as
subckt_cls_all = Symsubcircuit.getSubckts()
In this example the variable subckt_cls_all would be a dictionary with keys that are subcircuit names and values that are references to subcircuit classes that can be used for instance creation.
Subcircuit can be netlisted with netlist_SPICE or netlist_CDL methods. These methods netlist the subcircuit definition, not a particular instance.
from glow_utils import *
inv_par = Symsubcircuit("inv_par", ['A', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'L' : 130e-9})
n = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : 'ppar("WN")', 'l' : 'ppar("L")'})
p = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : '1.5*ppar("WN")', 'l' : 'ppar("L")'})
inv_par.addElement([n, p])
buff_par = Symsubcircuit("buff_par", ['in', 'out', 'VDD', 'VSS'], {'WN' : 1e-6})
inv1 = inv_par("inv1", ['in', 'net1', 'VDD', 'VSS'], {'WN' : 'ppar("WN")'})
inv2 = inv_par("inv2", ['net1', 'out', 'VDD', 'VSS'], {'WN' : '2*ppar("WN")'})
buff_par.addElement([inv1, inv2])
print(buff_par.netlist_SPICE())
The output of previous Python code is
.subckt buff_par in out VDD VSS WN=1e-06
Xinv1 in net1 VDD VSS inv_par WN={WN}
Xinv2 net1 out VDD VSS inv_par WN={2*WN}
.ends
Subcircuit can be flattened as
flat = buff_par.flat()
print(flat.netlist_SPICE())
The flattened circuit netlist is
.subckt buff_par_flat in out VDD VSS WN=1e-06
Minv1N0 net1 in VSS VSS sg13g2_lvnmos w=1e-06 l=1.3e-07 ad=3.1e-13 as=3.1e-13 pd=2.62e-06 ps=2.62e-06
Minv1P0 net1 in VDD VDD sg13g2_lvpmos w=1.5e-06 l=1.3e-07 ad=4.65e-13 as=4.65e-13 pd=3.62e-06 ps=3.62e-06
Minv2N0 out net1 VSS VSS sg13g2_lvnmos w=2e-06 l=1.3e-07 ad=6.2e-13 as=6.2e-13 pd=4.62e-06 ps=4.62e-06
Minv2P0 out net1 VDD VDD sg13g2_lvpmos w=3e-06 l=1.3e-07 ad=9.3e-13 as=9.3e-13 pd=6.62e-06 ps=6.62e-06
.ends
Device and node names in a flat circuit are built as hierarchical names, concatenating the names of instance names at each level of hierarchy, and can be impractically long.
Subcircuit method anonimize can be used to assign short names to devices and nets by assigning them sequential integer names.
Only the nets connecting to subcircuit terminals are not renamed to preserve meaningfull names.
Continuing the buffer example, calling
flat.anonimize()
print(flat.netlist_SPICE())
results in
.subckt buff_par_flat in out VDD VSS WN=1e-06
MN0 n0 in VSS VSS sg13g2_lvnmos w=1e-06 l=1.3e-07 ad=3.1e-13 as=3.1e-13 pd=2.62e-06 ps=2.62e-06
MP0 n0 in VDD VDD sg13g2_lvpmos w=1.5e-06 l=1.3e-07 ad=4.65e-13 as=4.65e-13 pd=3.62e-06 ps=3.62e-06
MN1 out n0 VSS VSS sg13g2_lvnmos w=2e-06 l=1.3e-07 ad=6.2e-13 as=6.2e-13 pd=4.62e-06 ps=4.62e-06
MP1 out n0 VDD VDD sg13g2_lvpmos w=3e-06 l=1.3e-07 ad=9.3e-13 as=9.3e-13 pd=6.62e-06 ps=6.62e-06
.ends
Symcheck class
Symcheck class implements various circuit level checks and utility functions that faciliate circuit inspection and verification.
It works only on flat circuits so the circuit should be flattened before use.
Class is used by instantiating an object with a circuit to be inspected as an argument
from glow_utils.symcheck import Symcheck
from glow_utils.symsubcircuit import Symsubcircuit
from glow_utils.symmosfet import SymNMOS, SymPMOS
inv_par = Symsubcircuit("inv_par", ['A', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'WP' : 450e-9, 'L' : 130e-9, 'NGN' : 1, 'NGP' : 1})
n = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : 'ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
p = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
inv_par.addElement([n, p])
buff_par = Symsubcircuit("buff_par", ['A', 'Y', 'VDD', 'VSS', ], {'WN' : 1e-6, 'WP' : 2e-6, 'NGN' : 2, 'NGP' : 2})
inv1 = inv_par("inv1", ['A', 'net1', 'VDD', 'VSS'], {'WN' : '1.5*ppar("WN")', 'WP' : '1.75*ppar("WP")', 'NGN' : 2, 'NGP' : 2, 'L' : 130e-9})
inv2 = inv_par("inv2", ['net1', 'Y', 'VDD', 'VSS'], {'WN' : '2*ppar("WN")', 'WP' : '2*ppar("WP")', 'NGN' : 4, 'NGP' : 4, 'L' : 130e-9})
buff_par.addElement([inv1, inv2])
flat_buff = buff_par.flat()
check = Symcheck(flat_buff)
Basic usage of Symcheck is to identify circuit inputs, outputs, power and ground nets:
id = check.identifyTerminals()
print("Inputs : ", " ".join(id['I']))
print("Outputs : ", " ".join(id['O']))
print("Power : ", " ".join(id['P']))
print("Ground : ", " ".join(id['G']))
Previous code produces output
Inputs : A
Outputs : Y
Power : VDD
Ground : VSS
Node connectivity is identified by examining the circuit: power node is identified as the node connected to PMOS bulks, ground node is identified as the node connected to NMOS bulks. Input nodes are connected to gates and circuit terminals, while ouptut nodes are connected to drain or source and circuit terminals. Node identification can be carried out as operatons on sets, so it is not computationaly expensive.
Electrical Rules Check (ERC) can be performed on a circuit by running
ercOK = check.ERC()
If all ERC checks pass the result is ercOK = True. If any of checks fail, the result is ercOK = False and the error message(s) is (are) printed.
ERC performs the following checks:
- Check if there are multiple nodes connected to NMOS or PMOS bulks. This is a so-called ‘soft bulk’ connect error, where NMOS or PMOS bulk conctacts are connected to different nets. This is a violation of electrical rules because all NMOS bulks are connected via conductive substrate, so there would be an electrical short between nets connected to bulks. The same happens in the case of PMOS transistors, where bulks are connected via conductive N well.
- Check if gate is directly connected to ground or power. This rule is an ERC error because power and ground nets form a large metal area in all metal layers, and connecting a transistor gate directly to ground or power net would result in antenna violation. If there is a need to connect a gate to constant ‘1’ or ‘0’ special cells should be used -
tie_hifor logic ‘1’ ortie_lowfor logic ‘0’. - Check if there are floating gates. Floating gates are not permitted as they result in unpredictable behavior due to random gate voltage. Gates can only be connected to drains or sources of other transistors or to subcircuit terminals.
Symsim class
Symsim class implements a simple logic simulator that can simulate static CMOS logic gates on a transistor level.
An example of Symsim usage is shown in the Python code below.
from glow_utils import *
nand2_par = Symsubcircuit("nand2_par", ['A', 'B', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'WP' : 450e-9, 'L' : 130e-9, 'NGN' : 1, 'NGP' : 1})
n0 = SymNMOS("N0", ['n0', 'A', 'VSS', 'VSS'], {'w' : '2*ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
n1 = SymNMOS("N1", ['Y', 'B', 'n0', 'VSS'], {'w' : '2*ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
p0 = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
p1 = SymPMOS("P1", ['Y', 'B', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
nand2_par.addElement([n0, n1, p0, p1])
sim = Symsim(nand2_par)
logicExpr = sim.combFunc()
print("Gate logic function is", logicExpr[0])
In this example a two input NAND gate nand2_par is made from NMOS and PMOS transistors.
The circuit is then used as an argument to Symsim constructor to create the sim instance.
Constructor method performs circuit elaboration: identifies circuit inputs, outputs and power and ground nets and performs an ERC checks.
After elaboration, a dictionary of all nodes in a given circuit is created and initialized.
Boolean function of a given combinatorial circuit is determined by executing combFunc method of sim.
The combFunc method calls the combSim method to simulate the circuit for all possible input values, and uses
input and output values to determine a symbolic Boolean expression.
In case of circuits with multiple outputs a Boolean expression is determined for each output.
For the previous example the output of the Python script is
Symsim::Elaborate: Circuit is flat.
Symsim::Elaborate: Circuit passes ERC.
Symsim::Elaborate: Inputs : B A
Symsim::Elaborate: Outputs : Y
Symsim::Elaborate: Power : VDD
Symsim::Elaborate: Ground : VSS
Symsim::Elaborate: Nodes : VSS VDD n0 A Y B
Symsim::Elaborate: Elaboration OK.
****************************************
Determining gate logic function.
Symsim::combSim: Simulating circuit with 2 inputs and 1 outputs.
Symsim::combSim: | 11 | 0
Symsim::combSim: | 10 | 1
Symsim::combSim: | 01 | 1
Symsim::combSim: | 00 | 1
Gate logic function is ~A | ~B
The expression ~A | ~B is equivalent to NAND gate function ~(A & B), so the circuit works as intended.
In a simple example of NAND gate it is easy to see that ~A | ~B = ~(A & B), but it might not be trivial
to establish equivalence for complex expressions, so it would be desirable to perform the check by code.
The following code does exatly that - performs an equivalency check of two Boolean expressions
from sympy import bool_map, Nand, Nor
from sympy.abc import x, y
expectedFn = Nand(x, y)
mapping = bool_map(expectedFn, logicExpr[0])
The function bool_map tries to match two boolean expressions and returns variable correspondence information if two expressions are equivalent, or None if they are not equivalent.
Simulation of a circuit logic function is performed in the combSim method. The simulation algorithm is straight forward: it goes through all circuit elements, gets the values of circuit nodes connected to device terminals, and calls the device sim method that calculates new node values.
Since the change of node values can result in change of other node values, the evaluation is performed in a ‘delta-cycle’ manner until all node values settle to their final values.
Device code that evaluates new node values is also simple: if the gate of an NMOS (PMOS) device is at high (low) value the device is evaluated, otherwise the node values are not changed. When NMOS or PMOS device is turned on, and there is no current flowing - which is true for static CMOS logic - the drain and source terminals are at the same potential. Since drain and source should be at the same potential, the new value of source and drain terminal nodes is determined by IEEE1164 rules for signal resolution. If there is a conflicting condition, such as a direct path from power to ground, the node values would resolve to ‘X’ and indicate that there is a problem with a circuit.
The folloing example shows a malformed circuit that can create a short between power and ground:
from glow_utils import *
# This circuit exhibits a short circuit between VDD and VSS for input values A=1, B=0
shortcircuit_par = Symsubcircuit("shortcircuit_par", ['A', 'B', 'Y', 'VDD', 'VSS'], {'WN' : 300e-9, 'WP' : 450e-9, 'L' : 130e-9, 'NGN' : 1, 'NGP' : 1})
n0 = SymNMOS("N0", ['Y', 'A', 'VSS', 'VSS'], {'w' : '2*ppar("WN")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGN")'})
p0 = SymPMOS("P0", ['Y', 'A', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
p1 = SymPMOS("P1", ['Y', 'B', 'VDD', 'VDD'], {'w' : 'ppar("WP")', 'l' : 'ppar("L")', 'ng' : 'ppar("NGP")'})
shortcircuit_par.addElement([n0, p0, p1])
sim = Symsim(shortcircuit_par)
print("*"*40)
print("Determining gate logic function.")
logicExpr = sim.combFunc()
print("Gate logic function is", logicExpr[0])
if sim.error:
print("ERROR : There is an error in the circuit.")
else:
print("Circuit is OK")
The circuit is similar to two input NAND gate, but with only one NMOS transistor N0 that conducts whenever input A is high. If input B is low the transistor P1 is turned on, forming a short circuit between power and ground through N0 and P1.
Short condition can happen when pull-up part of the logic gate that is made with PMOS transistors is not
complimentary to pull-down part made with NMOS transistors.
Running the previous Python code prints the following output:
Symsim::Elaborate: Circuit is flat.
Symsim::Elaborate: Circuit passes ERC.
Symsim::Elaborate: Inputs : A B
Symsim::Elaborate: Outputs : Y
Symsim::Elaborate: Power : VDD
Symsim::Elaborate: Ground : VSS
Symsim::Elaborate: Nodes : Y VSS A B VDD
Symsim::Elaborate: Elaboration OK.
****************************************
Determining gate logic function.
Symsim::combSim: Simulating circuit with 2 inputs and 1 outputs.
Symsim::combSim: | 11 | 0
Symsim::simstep: WARNING : Nodes contain invalid values.
Symsim::combSim: | 10 | X
Symsim::simstep: WARNING : Nodes contain invalid values.
Symsim::combSim: | 01 | X
Symsim::simstep: WARNING : Nodes contain invalid values.
Symsim::combSim: | 00 | X
Gate logic function is False
ERROR : There is an error in the circuit.
Short circuit condition occurs in the second simulation step for the value of inputs [A, B] = [1, 0] and it is indicated by the value X at the output.
Occurence of illegal values, such as X sets the value of variable error = True, that persists until the simulation is re-initialized.
This flag can, and should, be checked after a simulation to ensure that there are no issues with a circuit.
In the presented case, the X value is visible at the output, but there might be a problem with an internal node that does not interact with the output and might not be discovered without detailed examination of all node values at all simulation steps.
The error flag captures that something went wrong, and that detailed examination should be performed.