658 lines
37 KiB
HTML
658 lines
37 KiB
HTML
<html>
|
|
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
|
<title>Polygon Usage</title>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>Layout Versus Schematic Tutorial</h1>
|
|
<p>In this tutorial we will implement a toy VLSI layout verification
|
|
application. In VLSI CAD an important step of design is the sign off check
|
|
that verifies that the physical layout as drawn by mask designers and automated
|
|
tools implements the logical schematic specified by design engineers.
|
|
Physical layout is modeled as polygons on layers that are used to print the
|
|
layout to the silicon wafer during manufacture. It is much better to find
|
|
physical design mistakes before spending millions of dollars to prepare
|
|
lithography masks and run a test lot of wafers.</p>
|
|
<p>Real layout file formats are binary and often compressed and represent a
|
|
folded hierarchical model of layout where a group of polygons can be grouped
|
|
into a cell and instantiated as a group into other cells. For this
|
|
tutorial we assume a simplified ascii layout file format with no design
|
|
hierarchy, which we would call "flat" in VLSI jargon. Similarly we assume
|
|
a flat, ascii logical schematic net list file format. A net is a named
|
|
electrical connection in a circuit design. The goal of the layout
|
|
verification tutorial is to parse these two file formats, apply geometry
|
|
operations provided by Boost.Polygon on the layout data to generate a logical
|
|
schematic that represents what is implemented in the physical layout and then
|
|
compare the input schematic with the generated schematic to determine whether
|
|
they are the same.</p>
|
|
<p>First let us define some objects that we will need in the design of our toy
|
|
layout verification application:</p>
|
|
<p>Layout Rectangle: An axis-parallel rectangle with a layer associated<br>
|
|
Layout Pin: An axis-parallel rectangle with a layer and net (electrical signal)
|
|
name associated<br>
|
|
Layout Database: An associative container of layer name to polygon set<br>
|
|
Connectivity Database: An associative container of net name to layout database<br>
|
|
Physical Device: A specific geometric arrangement on several layers with one or
|
|
more input net and one output net<br>
|
|
Logical Net: A named graph node<br>
|
|
Logical Device: An un-named graph node with a device type<br>
|
|
Logical Pin: A special net that defines an input or output for the circuit<br>
|
|
Schematic Database: A graph consisting of nets and logical
|
|
devices</p>
|
|
<p>Next let's define the sequence of operations performed by our toy
|
|
layout versus schematic application:</p>
|
|
<p>Parse Layout: Stream layout rectangles and polygons into a layout database
|
|
and stream layout pins into a connectivity database<br>
|
|
Extract Connectivity: Add polygons from layout database to connectivity database
|
|
by physical touch or overlap relationship<br>
|
|
Extract Devices: Populate a schematic database with logical devices based on
|
|
physical devices identified within the layout geometry and extract their
|
|
terminals from the
|
|
connectivity database<br>
|
|
Extract Net List: Complete graph represented in schematic database derived from
|
|
layout<br>
|
|
Parse Schematic: Stream logical nets, devices and pins into a schematic
|
|
database<br>
|
|
Compare Schematics: Evaluate whether extracted schematic database is equivalent
|
|
to input schematic database and output result</p>
|
|
<p>To test our application we will extract single logic gates. A logic
|
|
gate is several transistors that work together to perform a specific logic
|
|
function. Logic functions include the commonly understood Boolean logic
|
|
operations such as Boolean AND, OR, XOR and INVERT. Also frequently used
|
|
are NAND and NOR, which are respectively AND and OR operations followed by an
|
|
INVERT operation. A NAND gate can be implemented in the CMOS circuit
|
|
family with four transistors. The NAND gate has two inputs and one output.
|
|
Each input goes to two transistors, one p-type transistor and one n-type
|
|
transistor. The "p" stands for positive and the "n" stands for negative.
|
|
When the p-type transistor is on
|
|
it pulls the output up to the same voltage as the voltage source. When the
|
|
n-type transistor is on it pulls the output down to the same voltage as the
|
|
ground. The process of creating a p-type transistor begins by "doping" the silicon
|
|
substrate to create n-type material. This area of n-type material will be
|
|
called the NWELL layer in our test data. Within the area of NWELL a
|
|
p-diffusion area is created by further doping the silicon to create p-type
|
|
material. This area of p-type material will be called PDIFF in our test
|
|
data. Through the middle of a PDIFF area bars of poly-silicon are grown
|
|
that create conductive lines over the diffusion area. The area of
|
|
poly-silicon material will be called POLY in our test data. Under some of these
|
|
poly-silicon lines a thin layer of silicon-oxide provides insulation but allows
|
|
the voltage field of the poly-silicon to interact with the diffusion. Each
|
|
of these insulated poly-silicon lines is the "gate" of a transistor.
|
|
The gate area will be called GATE in our test data. When the
|
|
voltage at the gate is the same as the ground voltage the p-type transistor is
|
|
"on" and can pass current from the voltage source to the output . The
|
|
poly-silicon lines that are not insulated create electrical connections to the
|
|
transistor for output signals and source voltage. The n-type transistor
|
|
differs from the p-type in that its diffusion is n-type material outside of NWELL area. Current can pass from the output to the ground when the
|
|
voltage at the gate of the n-type transistor is at the source voltage level.
|
|
Above the poly-silicon layer is a layer of silicon-oxide insulator with holes
|
|
cut out of it that get filled in with metal. These metal filled holes are
|
|
called vias and we will refer to this layer as VIA0 in our test data. On
|
|
top of VIA0 is a layer of metal polygons with silicon oxide insulator between
|
|
them. These metal polygons are wires and we will call them METAL1 in our
|
|
test data. The Layout Pins in our test data will be on METAL1. In a
|
|
NAND gate the two n-type transistors are configured in series, meaning that the
|
|
output of one is the input voltage source of the other. Only if both
|
|
n-type transistors of a NAND gate are "on" will the output be connected to
|
|
ground, signifying a logical "false". The two p-type transistors in a NAND
|
|
gate are configured in parallel. If either input to the NAND gate is a
|
|
logical "false" the p-type transistor it is connected to will be "on" and the
|
|
output of the gate will be a logical "true" because the transistor will connect
|
|
it to the voltage supply. The diagram below is an example of how a NAND
|
|
gate might be laid out and is not drawn to scale for any real process
|
|
technology. The diffusion material is intended to be cut away under the
|
|
gate material by a Boolean NOT operation and is represented as solid bars under
|
|
the gates of transistors only for convenience of drawing.</p>
|
|
<p>
|
|
<img border="0" src="images/NAND.PNG" width="602" height="387"></p>
|
|
<p>The following is the input layout file for the above NAND gate layout,
|
|
rectangle format is XL XH YL YH:</p>
|
|
<p><font face="Courier New" size="2">Rectangle 0 60 24 48 NWELL<br>
|
|
Rectangle 3 57 32 43 PDIFF<br>
|
|
Rectangle 3 57 5 16 NDIFF<br>
|
|
Rectangle 5 7 0 17 POLY<br>
|
|
Rectangle 5 7 22 45 POLY<br>
|
|
Rectangle 17 19 3 45 POLY<br>
|
|
Rectangle 29 31 31 48 POLY<br>
|
|
Rectangle 41 43 3 45 POLY<br>
|
|
Rectangle 53 55 3 45 POLY<br>
|
|
Rectangle 17 19 4 17 GATE<br>
|
|
Rectangle 17 19 31 44 GATE<br>
|
|
Rectangle 41 43 4 17 GATE<br>
|
|
Rectangle 41 43 31 44 GATE<br>
|
|
Rectangle 5 7 0 2 VIA0<br>
|
|
Rectangle 5 7 23 25 VIA0<br>
|
|
Rectangle 17 19 28 30 VIA0<br>
|
|
Rectangle 29 31 46 48 VIA0<br>
|
|
Rectangle 41 43 18 20 VIA0<br>
|
|
Rectangle 53 55 23 25 VIA0<br>
|
|
Rectangle 0 60 0 2 METAL1<br>
|
|
Rectangle 3 57 28 30 METAL1<br>
|
|
Rectangle 0 60 46 48 METAL1<br>
|
|
Rectangle 3 57 18 20 METAL1<br>
|
|
Rectangle 3 57 23 25 METAL1<br>
|
|
Pin 29 31 0 2 METAL1 GND<br>
|
|
Pin 29 31 23 25 METAL1 OUTPUT</font><font face="Courier New" size="2"><br>
|
|
Pin 29 31 28 30 METAL1 INPUT1</font><font face="Courier New" size="2"><br>
|
|
Pin 29 31 46 48 METAL1 VDD<br>
|
|
Pin 29 31 18 20 METAL1 INPUT2</font></p>
|
|
<p>
|
|
<img border="0" src="images/nands.PNG" width="421" height="402"></p>
|
|
<p>The following is the logic schematic net list file for the above NAND gate
|
|
schematic:</p>
|
|
<p><font face="Courier New" size="2">Pin OUTPUT<br>Pin INPUT1<br>Pin INPUT2<br>
|
|
Pin VDD<br>Pin GND<br>Device PTRANS VDD INPUT1 OUTPUT<br>Device PTRANS VDD
|
|
INPUT2 OUTPUT<br>Device NTRANS GND INPUT1 NET1<br>Device NTRANS NET1 INPUT2
|
|
OUTPUT</font></p>
|
|
<p>A human can look at this schematic and compare it to the drawn layout of the
|
|
NAND gate above to verify that the drawn layout matches what the schematic says
|
|
in a few seconds. If you do that now you will probably find the p and
|
|
n-type transistors and trace the connectivity of the inputs and power to the
|
|
terminals of the transistors and then to the output. Since there are on
|
|
the order of one billion transistors on a single chip these days we need to go a
|
|
lot faster than humans can inspect layout and make fewer mistakes. Using polygon set operations
|
|
and polygon connectivity extraction provided by Boost.Polygon we will automate
|
|
the identification of transistors and the tracing of connectivity. Based
|
|
on this
|
|
<a href="analysis.htm">analysis</a> of Boost.Polygon performance we can expect
|
|
this methodology to easily scale up to million gate blocks on standard
|
|
workstations and arbitrarily large designs given sufficient system memory.
|
|
let's start
|
|
by implementing some data structures for our application.</p>
|
|
<p><font face="Courier New" size="2">struct layout_rectangle {<br>
|
|
int xl, yl, xh, yh;<br>
|
|
std::string layer;<br>
|
|
};</font></p>
|
|
<p>Our layout rectangle is nice and minimal, just enough to store its data.
|
|
It is defined in <a href="tutorial/layout_rectangle.hpp">layout_rectangle.hpp</a>. Next
|
|
let's implement the layout
|
|
pin in a similar way.</p>
|
|
<p><font face="Courier New" size="2">struct layout_pin {<br>
|
|
int xl, yl, xh, yh;<br>
|
|
std::string layer;<br>
|
|
std::string net;<br>
|
|
};</font></p>
|
|
<p>Our layout pin is defined in <a href="tutorial/layout_pin.hpp">layout_pin.hpp</a>. Now
|
|
let's define a layout database object and populate it from our parsed
|
|
layout data in in <a href="tutorial/layout_database.hpp">layout_database.hpp</a>.</p>
|
|
<p><font face="Courier New" size="2">typedef std::map<std::string,
|
|
boost::polygon::polygon_90_set_data<int> > layout_database;<br>
|
|
<br>
|
|
//map the layout rectangle data type to the boost::polygon::rectangle_concept<br>
|
|
namespace boost { namespace polygon{<br>
|
|
template <><br>
|
|
struct rectangle_traits<layout_rectangle> {<br>
|
|
typedef int coordinate_type;<br>
|
|
typedef interval_data<int> interval_type;<br>
|
|
static inline interval_type get(const layout_rectangle&
|
|
rectangle, orientation_2d orient) {<br>
|
|
if(orient == HORIZONTAL)<br>
|
|
return interval_type(rectangle.xl,
|
|
rectangle.xh);<br>
|
|
return interval_type(rectangle.yl, rectangle.yh);<br>
|
|
}<br>
|
|
};<br>
|
|
<br>
|
|
template <><br>
|
|
struct geometry_concept<layout_rectangle> { typedef rectangle_concept
|
|
type; };<br>
|
|
}}<br>
|
|
<br>
|
|
//insert layout rectangles into a layout database<br>
|
|
inline void populate_layout_database(layout_database& layout, std::vector<layout_rectangle>&
|
|
rects) {<br>
|
|
for(std::size_t i = 0; i < rects.size(); ++i) {<br>
|
|
layout[rects[i].layer].insert(rects[i]);<br>
|
|
}<br>
|
|
}</font></p>
|
|
<p>We don't need to insert pins into the layout database because it doesn't know
|
|
anything about connectivity, just geometry. However, we do need to know
|
|
something about connectivity to compare a schematic to a layout, so we need to
|
|
define our connectivity database and some logical objects. First we define
|
|
an object for a logical device in <a href="tutorial/device.hpp">device.hpp</a>.
|
|
Since we are lazy this object does double duty as a pin and both types of
|
|
transistor. A traditional object oriented design might declare a base
|
|
class with virtual destructor and derive every device from that. Since we
|
|
aren't paid by the line of code let's just keep things simple.</p>
|
|
<p><font face="Courier New" size="2">struct device {<br>
|
|
std::string type;<br>
|
|
std::vector<std::string> terminals;<br>
|
|
};</font></p>
|
|
<p>Now let's define a schematic database object in
|
|
<a href="tutorial/schematic_database.hpp">schematic_database.hpp</a> and populate it from our parsed
|
|
schematic data.</p>
|
|
<p><font face="Courier New"><font size="2">struct schematic_database{<br>
|
|
std::vector<device> devices;<br>
|
|
std::map<std::string, std::set<std::size_t> > nets;<br>
|
|
};<br>
|
|
<br>
|
|
//given a vector of devices populate the map of net name to set of device index<br>
|
|
inline void extract_netlist(std::map<std::string, std::set<std::size_t> >& nets,<br>
|
|
|
|
std::vector<device>& devices) {<br>
|
|
for(std::size_t i = 0; i < devices.size(); ++i) {<br>
|
|
for(std::size_t j = 0; j < devices[i].terminals.size(); ++j)
|
|
{<br>
|
|
//create association between net name and device
|
|
id<br>
|
|
|
|
nets[devices[i].terminals[j]].insert(nets[devices[i].terminals[j]].end(), i);<br>
|
|
}<br>
|
|
}<br>
|
|
}</font></font></p>
|
|
<p>Our schematic database is just a vector of devices, which are associated to
|
|
nets by name through their terminals and a map of net name to set of device
|
|
index into the vector, which completes the graph by associating nets with their
|
|
devices. Given the devices and their terminal nets we easily build the
|
|
mapping from nets to devices with the extract_netlist operation. Now we
|
|
are ready to start working on extracting our layout to a derived schematic
|
|
database. However, first we need to build a physical connectivity database
|
|
with geometry in it before we can build a logical connectivity database from the
|
|
layout. We define a simple connectivity database in
|
|
<a href="tutorial/connectivity_database.hpp">connectivity_database.hpp</a> as a
|
|
map of net name to layout database of geometry connected to that net and
|
|
populate it with the layout database and pin data.</p>
|
|
<p><font size="2" face="Courier New">typedef std::map<std::string,
|
|
layout_database > connectivity_database;<br>
|
|
<br>
|
|
//map layout pin data type to boost::polygon::rectangle_concept<br>
|
|
namespace boost { namespace polygon{<br>
|
|
template <><br>
|
|
struct rectangle_traits<layout_pin> {<br>
|
|
typedef int coordinate_type;<br>
|
|
typedef interval_data<int> interval_type;<br>
|
|
static inline interval_type get(const layout_pin& pin,
|
|
orientation_2d orient) {<br>
|
|
if(orient == HORIZONTAL)<br>
|
|
return interval_type(pin.xl, pin.xh);<br>
|
|
return interval_type(pin.yl, pin.yh);<br>
|
|
}<br>
|
|
};<br>
|
|
<br>
|
|
template <><br>
|
|
struct geometry_concept<layout_pin> { typedef rectangle_concept type; };<br>
|
|
}}</font></p>
|
|
<p><font size="2" face="Courier New">//given a layout_database we populate a
|
|
connectivity database<br>
|
|
inline void populate_connectivity_database(connectivity_database& connectivity,<br>
|
|
|
|
std::vector<layout_pin>& pins, layout_database& layout) {<br>
|
|
using namespace boost::polygon;<br>
|
|
using namespace boost::polygon::operators;<br>
|
|
for(std::size_t i = 0; i < pins.size(); ++i) {<br>
|
|
connectivity[pins[i].net][pins[i].layer].insert(pins[i]);<br>
|
|
}<br>
|
|
int internal_net_suffix = 0;<br>
|
|
//connect metal1 layout to pins which were on metal1<br>
|
|
connect_layout_to_layer(connectivity, layout["METAL1"], "METAL1", <br>
|
|
|
|
"METAL1", "__internal_net_", internal_net_suffix);<br>
|
|
//connect via0 layout to metal1<br>
|
|
connect_layout_to_layer(connectivity, layout["VIA0"], "VIA0", <br>
|
|
|
|
"METAL1", "__internal_net_", internal_net_suffix);<br>
|
|
//poly needs to have gates subtracted from it to prevent shorting through
|
|
transistors<br>
|
|
polygon_set poly_not_gate = layout["POLY"] - layout["GATE"];<br>
|
|
//connect poly minus gate to via0<br>
|
|
connect_layout_to_layer(connectivity, poly_not_gate, "POLY", <br>
|
|
|
|
"VIA0", "__internal_net_", internal_net_suffix);<br>
|
|
//we don't want to short signals through transistors so we subtract the
|
|
gate regions<br>
|
|
//from the diffusions<br>
|
|
polygon_set diff_not_gate = (layout["PDIFF"] + layout["NDIFF"]) -
|
|
layout["GATE"];<br>
|
|
//connect diffusion minus gate to poly<br>
|
|
//Note that I made up the DIFF layer name for combined P and NDIFF<br>
|
|
connect_layout_to_layer(connectivity, diff_not_gate, "DIFF", <br>
|
|
|
|
"POLY", "__internal_net_", internal_net_suffix);<br>
|
|
//connect gate to poly to make connections through gates on poly<br>
|
|
connect_layout_to_layer(connectivity, layout["GATE"], "GATE", <br>
|
|
|
|
"POLY", "__internal_net_", internal_net_suffix);<br>
|
|
//now we have traced connectivity of the layout down to the transistor
|
|
level<br>
|
|
//any polygons not connected to pins have been assigned internal net
|
|
names<br>
|
|
}</font></p>
|
|
<p>This populate connectivity database function is our first real use of
|
|
Boost.Polygon in our application. Here we are doing Boolean (polygon set)
|
|
operations on layout layers to merge together the PDIFF and NDIFF layers and cut
|
|
away the GATE layer from the result, for example. We connect up the layout
|
|
starting from the pins and working our way down the layer stack to the
|
|
transistor level. It would work equally well to work our way up the layer
|
|
stack, or connect things up in any order, really, but this way produces fewer
|
|
internal temporary nets that need to be merged when connections between them are
|
|
discovered later. The connect layout to layer function used above needs to
|
|
be implemented before we can populate our connectivity database.</p>
|
|
<p><font size="2" face="Courier New">inline void
|
|
connect_layout_to_layer(connectivity_database& connectivity, polygon_set&
|
|
layout, <br>
|
|
|
|
std::string layout_layer, std::string layer,<br>
|
|
|
|
std::string net_prefix, int& net_suffix) {<br>
|
|
if(layout_layer.empty())<br>
|
|
return;<br>
|
|
boost::polygon::connectivity_extraction_90<int> ce;<br>
|
|
std::vector<std::string> net_ids;<br>
|
|
for(connectivity_database::iterator itr = connectivity.begin(); itr !=
|
|
connectivity.end(); ++itr) {<br>
|
|
net_ids.push_back((*itr).first);<br>
|
|
ce.insert((*itr).second[layer]);<br>
|
|
}<br>
|
|
std::vector<polygon> polygons;<br>
|
|
layout.get_polygons(polygons);<br>
|
|
std::size_t polygon_id_offset = net_ids.size();<br>
|
|
for(std::size_t i = 0; i < polygons.size(); ++i) {<br>
|
|
ce.insert(polygons[i]);<br>
|
|
}<br>
|
|
std::vector<std::set<int> > graph(polygons.size() + net_ids.size(),
|
|
std::set<int>());<br>
|
|
ce.extract(graph);<br>
|
|
std::vector<int> polygon_color(polygons.size() + net_ids.size(), 0);<br>
|
|
//for each net in net_ids populate connected component with net<br>
|
|
for(std::size_t node_id = 0; node_id < net_ids.size(); ++node_id) {<br>
|
|
populate_connected_component(connectivity, polygons,
|
|
polygon_color, graph, node_id, <br>
|
|
polygon_id_offset, net_ids[node_id], net_ids, <br>
|
|
net_prefix, layout_layer);<br>
|
|
}<br>
|
|
//for each polygon_color that is zero populate connected component with
|
|
net_prefix + net_suffix++<br>
|
|
for(std::size_t i = 0; i < polygons.size(); ++i) {<br>
|
|
if(polygon_color[i + polygon_id_offset] == 0) {<br>
|
|
std::stringstream ss(std::stringstream::in |
|
|
std::stringstream::out);<br>
|
|
ss << net_prefix << net_suffix++;<br>
|
|
std::string internal_net; <br>
|
|
ss >> internal_net;<br>
|
|
populate_connected_component(connectivity,
|
|
polygons, polygon_color, graph, <br>
|
|
i + polygon_id_offset, <br>
|
|
polygon_id_offset, internal_net, net_ids, <br>
|
|
net_prefix, layout_layer);<br>
|
|
}<br>
|
|
}<br>
|
|
}</font></p>
|
|
<p>The connect layout to layer function uses the connectivity extraction feature
|
|
of Boost.Polyon to build a connectivity graph for polygons on the input polygon
|
|
set and in the connectivity database on the specified layer. It then finds
|
|
polygons associated with existing nets in the connectivity database through
|
|
graph traversal and inserts them into the connectivity database. Finally,
|
|
polygons that weren't connected to existing nets are inserted into the
|
|
connectivity database on auto-generated internal net names. The insertion
|
|
of a connected component into the connectivity database is handled by the
|
|
recursive traversal of the connectivity graph that we implement next.</p>
|
|
<p><font size="2" face="Courier New">inline void populate_connected_component<br>
|
|
(connectivity_database& connectivity, std::vector<polygon>& polygons, <br>
|
|
std::vector<int> polygon_color, std::vector<std::set<int> >& graph, <br>
|
|
std::size_t node_id, std::size_t polygon_id_offset, std::string& net, <br>
|
|
std::vector<std::string>& net_ids, std::string net_prefix,<br>
|
|
std::string& layout_layer) {<br>
|
|
if(polygon_color[node_id] == 1)<br>
|
|
return;<br>
|
|
polygon_color[node_id] = 1;<br>
|
|
if(node_id < polygon_id_offset && net_ids[node_id] != net) {<br>
|
|
//merge nets in connectivity database<br>
|
|
//if one of the nets is internal net merge it into the other<br>
|
|
std::string net1 = net_ids[node_id];<br>
|
|
std::string net2 = net;<br>
|
|
if(net.compare(0, net_prefix.length(), net_prefix) == 0) {<br>
|
|
net = net1;<br>
|
|
std::swap(net1, net2);<br>
|
|
} else {<br>
|
|
net_ids[node_id] = net;<br>
|
|
}<br>
|
|
connectivity_database::iterator itr =
|
|
connectivity.find(net1);<br>
|
|
if(itr != connectivity.end()) {<br>
|
|
for(layout_database::iterator itr2 = (*itr).second.begin();<br>
|
|
itr2 != (*itr).second.end();
|
|
++itr2) {<br>
|
|
connectivity[net2][(*itr2).first].insert((*itr2).second);<br>
|
|
}<br>
|
|
connectivity.erase(itr);<br>
|
|
}<br>
|
|
}<br>
|
|
if(node_id >= polygon_id_offset)<br>
|
|
connectivity[net][layout_layer].insert(polygons[node_id -
|
|
polygon_id_offset]);<br>
|
|
for(std::set<int>::iterator itr = graph[node_id].begin();<br>
|
|
itr != graph[node_id].end(); ++itr) {<br>
|
|
populate_connected_component(connectivity, polygons,
|
|
polygon_color, graph, <br>
|
|
*itr, polygon_id_offset, net, net_ids, net_prefix,
|
|
layout_layer);<br>
|
|
}<br>
|
|
}<br>
|
|
<br>
|
|
</font>We want to merge internally generated nets into pin nets, which is the
|
|
most complicated part of this simple procedure. Now that we have our
|
|
connectivity database extracted from pins down to transistors we need to extract
|
|
our transistors and establish the relationship between transistor terminals and
|
|
nets in our connectivity database. First let's extract transistors with
|
|
the functions defined in defined in <a href="tutorial/extract_devices.hpp">
|
|
extract_devices.hpp</a>.</p>
|
|
<p><font size="2" face="Courier New">typedef boost::polygon::connectivity_extraction_90<int>
|
|
connectivity_extraction;<br>
|
|
inline std::vector<std::set<int> ><br>
|
|
extract_layer(connectivity_extraction& ce, std::vector<std::string>& net_ids,<br>
|
|
|
|
connectivity_database& connectivity, polygon_set& layout,<br>
|
|
|
|
std::string layer) {<br>
|
|
for(connectivity_database::iterator itr = connectivity.begin(); itr !=
|
|
connectivity.end(); ++itr) {<br>
|
|
net_ids.push_back((*itr).first);<br>
|
|
ce.insert((*itr).second[layer]);<br>
|
|
}<br>
|
|
std::vector<polygon> polygons;<br>
|
|
layout.get_polygons(polygons);<br>
|
|
for(std::size_t i = 0; i < polygons.size(); ++i) {<br>
|
|
ce.insert(polygons[i]);<br>
|
|
}<br>
|
|
std::vector<std::set<int> > graph(polygons.size() + net_ids.size(),
|
|
std::set<int>());<br>
|
|
ce.extract(graph);<br>
|
|
return graph;<br>
|
|
}</font></p>
|
|
<p>This extract layer algorithm constructs a connectivity graph between polygons
|
|
in the input polygon set and polygons in the given layer of the connectivity
|
|
database. It is used to form the association between transistors and their
|
|
terminal nets in the function for extracting a specific transistor type.</p>
|
|
<p><font size="2" face="Courier New">inline void extract_device_type(std::vector<device>&
|
|
devices, connectivity_database& connectivity,<br>
|
|
|
|
polygon_set& layout, std::string type) {<br>
|
|
//recall that P and NDIFF were merged into one DIFF layer in the
|
|
connectivity database<br>
|
|
//find the two nets on the DIFF layer that interact with each transistor<br>
|
|
//and then find the net on the poly layer that interacts with each
|
|
transistor<br>
|
|
boost::polygon::connectivity_extraction_90<int> cediff;<br>
|
|
std::vector<std::string> net_ids_diff;<br>
|
|
std::vector<std::set<int> > graph_diff =<br>
|
|
extract_layer(cediff, net_ids_diff, connectivity, layout,
|
|
"DIFF");<br>
|
|
boost::polygon::connectivity_extraction_90<int> cepoly;<br>
|
|
std::vector<std::string> net_ids_poly;<br>
|
|
std::vector<std::set<int> > graph_poly =<br>
|
|
extract_layer(cepoly, net_ids_poly, connectivity, layout,
|
|
"POLY");<br>
|
|
std::vector<device> tmp_devices(graph_diff.size() - net_ids_poly.size());<br>
|
|
for(std::size_t i = net_ids_poly.size(); i < graph_diff.size(); ++i) {<br>
|
|
tmp_devices[i - net_ids_diff.size()].type = type;<br>
|
|
tmp_devices[i - net_ids_diff.size()].terminals = std::vector<std::string>(3,
|
|
std::string());<br>
|
|
std::size_t j = 0;<br>
|
|
for(std::set<int>::iterator itr = graph_diff[i].begin();<br>
|
|
itr != graph_diff[i].end(); ++itr,
|
|
++j) {<br>
|
|
if(j == 0) {<br>
|
|
tmp_devices[i - net_ids_diff.size()].terminals[0]
|
|
= net_ids_diff[*itr];<br>
|
|
} else if(j == 1) {<br>
|
|
tmp_devices[i - net_ids_diff.size()].terminals[2]
|
|
= net_ids_diff[*itr];<br>
|
|
} else {<br>
|
|
//error, too many diff connections<br>
|
|
tmp_devices[i - net_ids_diff.size()].terminals
|
|
= std::vector<std::string>(3, std::string());<br>
|
|
}<br>
|
|
}<br>
|
|
j = 0;<br>
|
|
for(std::set<int>::iterator itr = graph_poly[i].begin();<br>
|
|
itr != graph_poly[i].end(); ++itr,
|
|
++j) {<br>
|
|
if(j == 0) {<br>
|
|
tmp_devices[i - net_ids_diff.size()].terminals[1]
|
|
= net_ids_poly[*itr];<br>
|
|
} else {<br>
|
|
//error, too many poly connections<br>
|
|
tmp_devices[i - net_ids_poly.size()].terminals
|
|
= std::vector<std::string>(3, std::string());<br>
|
|
}<br>
|
|
}<br>
|
|
}<br>
|
|
<br>
|
|
devices.insert(devices.end(), tmp_devices.begin(), tmp_devices.end());<br>
|
|
}</font></p>
|
|
<p>We append transistors onto the vector of devices with their terminals
|
|
populated with net names extracted from the connectivity database.
|
|
Transistors' terminals are connected through the POLY and DIFF layers where DIFF
|
|
contains both PDIFF and NDIFF. The connection to POLY layer is the gate of
|
|
the transistor while the connections to DIFF on either side of the channel of
|
|
the transistor are the source and drain. We can use this to extract are p
|
|
and n-type transistors. <font size="2" face="Courier New"><br>
|
|
<br>
|
|
//populates vector of devices based on connectivity and layout data<br>
|
|
inline void extract_devices(std::vector<device>& devices, connectivity_database&
|
|
connectivity,<br>
|
|
|
|
layout_database& layout) {<br>
|
|
using namespace boost::polygon::operators;<br>
|
|
//p-type transistors are gate that interact with p diffusion and nwell<br>
|
|
polygon_set ptransistors = layout["GATE"];<br>
|
|
ptransistors.interact(layout["PDIFF"]);<br>
|
|
ptransistors.interact(layout["NWELL"]);<br>
|
|
//n-type transistors are gate that interact with n diffusion and not
|
|
nwell<br>
|
|
polygon_set ntransistors = layout["GATE"];<br>
|
|
ntransistors.interact(layout["NDIFF"]);<br>
|
|
polygon_set not_ntransistors = ntransistors;<br>
|
|
not_ntransistors.interact(layout["NWELL"]);<br>
|
|
ntransistors -= not_ntransistors;<br>
|
|
extract_device_type(devices, connectivity, ptransistors, "PTRANS");<br>
|
|
extract_device_type(devices, connectivity, ntransistors, "NTRANS");<br>
|
|
}</font></p>
|
|
<p>The extract devices procedure makes some more use of Boost.Polygon Boolean
|
|
operations on the layout data when we exclude GATE material over NDIFF that
|
|
isn't also over NWELL to extract our n-type transistors. We also are using
|
|
the "interact" operation on polygon gets, which is implemented in terms of
|
|
connectivity extraction and retains all polygons of a polygon set that touch or
|
|
overlap polygons from another polygon set. Now that we have a vector of
|
|
devices we can build a schematic database by calling the extract_netlist
|
|
function. We can then compare the extracted schematic from the schematic
|
|
read in from file with the functions defined in
|
|
<a href="tutorial/compare_schematics.hpp">compare_schematics.hpp</a>.
|
|
Since comparing two schematics has no geometric aspect we won't go into that
|
|
procedure here in the tutorial and will skip to the integration of all these
|
|
procedures in defined in <a href="tutorial/extract.cpp">extract.cpp</a> to build
|
|
the layout to schematic comparison algorithm.</p>
|
|
<p><font size="2" face="Courier New">bool compare_files(std::string layout_file,
|
|
std::string schematic_file) {<br>
|
|
std::ifstream sin(schematic_file.c_str());<br>
|
|
std::ifstream lin(layout_file.c_str());<br>
|
|
<br>
|
|
std::vector<layout_rectangle> rects;<br>
|
|
std::vector<layout_pin> pins;<br>
|
|
parse_layout(rects, pins, lin);<br>
|
|
<br>
|
|
schematic_database reference_schematic;<br>
|
|
parse_schematic_database(reference_schematic, sin);<br>
|
|
<br>
|
|
layout_database layout;<br>
|
|
populate_layout_database(layout, rects);<br>
|
|
<br>
|
|
connectivity_database connectivity;<br>
|
|
populate_connectivity_database(connectivity, pins, layout);<br>
|
|
<br>
|
|
schematic_database schematic;<br>
|
|
std::vector<device>& devices = schematic.devices;<br>
|
|
for(std::size_t i = 0; i < pins.size(); ++i) {<br>
|
|
devices.push_back(device());<br>
|
|
devices.back().type = "PIN";<br>
|
|
devices.back().terminals.push_back(pins[i].net);<br>
|
|
}<br>
|
|
extract_devices(devices, connectivity, layout);<br>
|
|
extract_netlist(schematic.nets, devices);<br>
|
|
return compare_schematics(reference_schematic, schematic);<br>
|
|
}</font></p>
|
|
<p><font face="Courier New" size="2">int main(int argc, char **argv) {<br>
|
|
if(argc < 3) {<br>
|
|
std::cout << "usage: " << argv[0] << " <layout_file> <schematic_file>"
|
|
<< std::endl;<br>
|
|
return -1;<br>
|
|
}<br>
|
|
bool result = compare_files(argv[1], argv[2]);<br>
|
|
if(result == false) {<br>
|
|
std::cout << "Layout does not match schematic." << std::endl;<br>
|
|
return 1;<br>
|
|
} <br>
|
|
std::cout << "Layout does match schematic." << std::endl;<br>
|
|
return 0;<br>
|
|
}<br>
|
|
<br>
|
|
</font>We test the program with two schematics and three layouts. These
|
|
include a nand and a nor gate layout and schematic as well as an incorrect nand
|
|
gate layout. The nand layout and schematic are the same as shown above.<font face="Courier New" size="2">
|
|
</font></p>
|
|
<p><font face="Courier New" size="2">> lvs<br>
|
|
usage: lvs <layout_file> <schematic_file><br>
|
|
> lvs nand.layout nand.schematic <br>
|
|
Layout does match schematic.<br>
|
|
> lvs nand_short.layout nand.schematic <br>
|
|
Layout does not match schematic.<br>
|
|
> lvs nand.layout nor.schematic <br>
|
|
Layout does not match schematic.<br>
|
|
> lvs nor.layout nor.schematic <br>
|
|
Layout does match schematic.<br>
|
|
> lvs nor.layout nand.schematic <br>
|
|
Layout does not match schematic.</font></p>
|
|
<p>This concludes our tutorial on how to build a simple layout to schematic
|
|
verification application based on Boost.Polygon library capabilities. The
|
|
implementation of this application made many simplifying assumptions that are
|
|
not valid in the real world and hard coded a lot of things that need to be
|
|
configurable in a real layout verification application. However, it does
|
|
give an idea of how to use Boost.Polygon to solve real problems and points in
|
|
the direction of how a real application might use Boost.Polygon.</p>
|
|
|
|
|
|
<table class="docinfo" rules="none" frame="void" id="table1">
|
|
<colgroup>
|
|
<col class="docinfo-name"><col class="docinfo-content">
|
|
</colgroup>
|
|
<tbody vAlign="top">
|
|
<tr>
|
|
<th class="docinfo-name">Copyright:</th>
|
|
<td>Copyright � Intel Corporation 2008-2010.</td>
|
|
</tr>
|
|
<tr class="field">
|
|
<th class="docinfo-name">License:</th>
|
|
<td class="field-body">Distributed under the Boost Software License,
|
|
Version 1.0. (See accompanying file <tt class="literal">
|
|
<span class="pre">LICENSE_1_0.txt</span></tt> or copy at
|
|
<a class="reference" target="_top" href="http://www.boost.org/LICENSE_1_0.txt">
|
|
http://www.boost.org/LICENSE_1_0.txt</a>)</td>
|
|
</tr>
|
|
</table>
|
|
|
|
</body>
|
|
|
|
</html> |