]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | <html> |
2 | ||
3 | <head> | |
4 | <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> | |
5 | <title>Polygon Usage</title> | |
6 | </head> | |
7 | ||
8 | <body> | |
9 | ||
10 | <h1>Layout Versus Schematic Tutorial</h1> | |
11 | <p>In this tutorial we will implement a toy VLSI layout verification | |
12 | application. In VLSI CAD an important step of design is the sign off check | |
13 | that verifies that the physical layout as drawn by mask designers and automated | |
14 | tools implements the logical schematic specified by design engineers. | |
15 | Physical layout is modeled as polygons on layers that are used to print the | |
16 | layout to the silicon wafer during manufacture. It is much better to find | |
17 | physical design mistakes before spending millions of dollars to prepare | |
18 | lithography masks and run a test lot of wafers.</p> | |
19 | <p>Real layout file formats are binary and often compressed and represent a | |
20 | folded hierarchical model of layout where a group of polygons can be grouped | |
21 | into a cell and instantiated as a group into other cells. For this | |
22 | tutorial we assume a simplified ascii layout file format with no design | |
23 | hierarchy, which we would call "flat" in VLSI jargon. Similarly we assume | |
24 | a flat, ascii logical schematic net list file format. A net is a named | |
25 | electrical connection in a circuit design. The goal of the layout | |
26 | verification tutorial is to parse these two file formats, apply geometry | |
27 | operations provided by Boost.Polygon on the layout data to generate a logical | |
28 | schematic that represents what is implemented in the physical layout and then | |
29 | compare the input schematic with the generated schematic to determine whether | |
30 | they are the same.</p> | |
31 | <p>First let us define some objects that we will need in the design of our toy | |
32 | layout verification application:</p> | |
33 | <p>Layout Rectangle: An axis-parallel rectangle with a layer associated<br> | |
34 | Layout Pin: An axis-parallel rectangle with a layer and net (electrical signal) | |
35 | name associated<br> | |
36 | Layout Database: An associative container of layer name to polygon set<br> | |
37 | Connectivity Database: An associative container of net name to layout database<br> | |
38 | Physical Device: A specific geometric arrangement on several layers with one or | |
39 | more input net and one output net<br> | |
40 | Logical Net: A named graph node<br> | |
41 | Logical Device: An un-named graph node with a device type<br> | |
42 | Logical Pin: A special net that defines an input or output for the circuit<br> | |
43 | Schematic Database: A graph consisting of nets and logical | |
44 | devices</p> | |
45 | <p>Next let's define the sequence of operations performed by our toy | |
46 | layout versus schematic application:</p> | |
47 | <p>Parse Layout: Stream layout rectangles and polygons into a layout database | |
48 | and stream layout pins into a connectivity database<br> | |
49 | Extract Connectivity: Add polygons from layout database to connectivity database | |
50 | by physical touch or overlap relationship<br> | |
51 | Extract Devices: Populate a schematic database with logical devices based on | |
52 | physical devices identified within the layout geometry and extract their | |
53 | terminals from the | |
54 | connectivity database<br> | |
55 | Extract Net List: Complete graph represented in schematic database derived from | |
56 | layout<br> | |
57 | Parse Schematic: Stream logical nets, devices and pins into a schematic | |
58 | database<br> | |
59 | Compare Schematics: Evaluate whether extracted schematic database is equivalent | |
60 | to input schematic database and output result</p> | |
61 | <p>To test our application we will extract single logic gates. A logic | |
62 | gate is several transistors that work together to perform a specific logic | |
63 | function. Logic functions include the commonly understood Boolean logic | |
64 | operations such as Boolean AND, OR, XOR and INVERT. Also frequently used | |
65 | are NAND and NOR, which are respectively AND and OR operations followed by an | |
66 | INVERT operation. A NAND gate can be implemented in the CMOS circuit | |
67 | family with four transistors. The NAND gate has two inputs and one output. | |
68 | Each input goes to two transistors, one p-type transistor and one n-type | |
69 | transistor. The "p" stands for positive and the "n" stands for negative. | |
70 | When the p-type transistor is on | |
71 | it pulls the output up to the same voltage as the voltage source. When the | |
72 | n-type transistor is on it pulls the output down to the same voltage as the | |
73 | ground. The process of creating a p-type transistor begins by "doping" the silicon | |
74 | substrate to create n-type material. This area of n-type material will be | |
75 | called the NWELL layer in our test data. Within the area of NWELL a | |
76 | p-diffusion area is created by further doping the silicon to create p-type | |
77 | material. This area of p-type material will be called PDIFF in our test | |
78 | data. Through the middle of a PDIFF area bars of poly-silicon are grown | |
79 | that create conductive lines over the diffusion area. The area of | |
80 | poly-silicon material will be called POLY in our test data. Under some of these | |
81 | poly-silicon lines a thin layer of silicon-oxide provides insulation but allows | |
82 | the voltage field of the poly-silicon to interact with the diffusion. Each | |
83 | of these insulated poly-silicon lines is the "gate" of a transistor. | |
84 | The gate area will be called GATE in our test data. When the | |
85 | voltage at the gate is the same as the ground voltage the p-type transistor is | |
86 | "on" and can pass current from the voltage source to the output . The | |
87 | poly-silicon lines that are not insulated create electrical connections to the | |
88 | transistor for output signals and source voltage. The n-type transistor | |
89 | 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 | |
90 | voltage at the gate of the n-type transistor is at the source voltage level. | |
91 | Above the poly-silicon layer is a layer of silicon-oxide insulator with holes | |
92 | cut out of it that get filled in with metal. These metal filled holes are | |
93 | called vias and we will refer to this layer as VIA0 in our test data. On | |
94 | top of VIA0 is a layer of metal polygons with silicon oxide insulator between | |
95 | them. These metal polygons are wires and we will call them METAL1 in our | |
96 | test data. The Layout Pins in our test data will be on METAL1. In a | |
97 | NAND gate the two n-type transistors are configured in series, meaning that the | |
98 | output of one is the input voltage source of the other. Only if both | |
99 | n-type transistors of a NAND gate are "on" will the output be connected to | |
100 | ground, signifying a logical "false". The two p-type transistors in a NAND | |
101 | gate are configured in parallel. If either input to the NAND gate is a | |
102 | logical "false" the p-type transistor it is connected to will be "on" and the | |
103 | output of the gate will be a logical "true" because the transistor will connect | |
104 | it to the voltage supply. The diagram below is an example of how a NAND | |
105 | gate might be laid out and is not drawn to scale for any real process | |
106 | technology. The diffusion material is intended to be cut away under the | |
107 | gate material by a Boolean NOT operation and is represented as solid bars under | |
108 | the gates of transistors only for convenience of drawing.</p> | |
109 | <p> | |
110 | <img border="0" src="images/NAND.PNG" width="602" height="387"></p> | |
111 | <p>The following is the input layout file for the above NAND gate layout, | |
112 | rectangle format is XL XH YL YH:</p> | |
113 | <p><font face="Courier New" size="2">Rectangle 0 60 24 48 NWELL<br> | |
114 | Rectangle 3 57 32 43 PDIFF<br> | |
115 | Rectangle 3 57 5 16 NDIFF<br> | |
116 | Rectangle 5 7 0 17 POLY<br> | |
117 | Rectangle 5 7 22 45 POLY<br> | |
118 | Rectangle 17 19 3 45 POLY<br> | |
119 | Rectangle 29 31 31 48 POLY<br> | |
120 | Rectangle 41 43 3 45 POLY<br> | |
121 | Rectangle 53 55 3 45 POLY<br> | |
122 | Rectangle 17 19 4 17 GATE<br> | |
123 | Rectangle 17 19 31 44 GATE<br> | |
124 | Rectangle 41 43 4 17 GATE<br> | |
125 | Rectangle 41 43 31 44 GATE<br> | |
126 | Rectangle 5 7 0 2 VIA0<br> | |
127 | Rectangle 5 7 23 25 VIA0<br> | |
128 | Rectangle 17 19 28 30 VIA0<br> | |
129 | Rectangle 29 31 46 48 VIA0<br> | |
130 | Rectangle 41 43 18 20 VIA0<br> | |
131 | Rectangle 53 55 23 25 VIA0<br> | |
132 | Rectangle 0 60 0 2 METAL1<br> | |
133 | Rectangle 3 57 28 30 METAL1<br> | |
134 | Rectangle 0 60 46 48 METAL1<br> | |
135 | Rectangle 3 57 18 20 METAL1<br> | |
136 | Rectangle 3 57 23 25 METAL1<br> | |
137 | Pin 29 31 0 2 METAL1 GND<br> | |
138 | Pin 29 31 23 25 METAL1 OUTPUT</font><font face="Courier New" size="2"><br> | |
139 | Pin 29 31 28 30 METAL1 INPUT1</font><font face="Courier New" size="2"><br> | |
140 | Pin 29 31 46 48 METAL1 VDD<br> | |
141 | Pin 29 31 18 20 METAL1 INPUT2</font></p> | |
142 | <p> | |
143 | <img border="0" src="images/nands.PNG" width="421" height="402"></p> | |
144 | <p>The following is the logic schematic net list file for the above NAND gate | |
145 | schematic:</p> | |
146 | <p><font face="Courier New" size="2">Pin OUTPUT<br>Pin INPUT1<br>Pin INPUT2<br> | |
147 | Pin VDD<br>Pin GND<br>Device PTRANS VDD INPUT1 OUTPUT<br>Device PTRANS VDD | |
148 | INPUT2 OUTPUT<br>Device NTRANS GND INPUT1 NET1<br>Device NTRANS NET1 INPUT2 | |
149 | OUTPUT</font></p> | |
150 | <p>A human can look at this schematic and compare it to the drawn layout of the | |
151 | NAND gate above to verify that the drawn layout matches what the schematic says | |
152 | in a few seconds. If you do that now you will probably find the p and | |
153 | n-type transistors and trace the connectivity of the inputs and power to the | |
154 | terminals of the transistors and then to the output. Since there are on | |
155 | the order of one billion transistors on a single chip these days we need to go a | |
156 | lot faster than humans can inspect layout and make fewer mistakes. Using polygon set operations | |
157 | and polygon connectivity extraction provided by Boost.Polygon we will automate | |
158 | the identification of transistors and the tracing of connectivity. Based | |
159 | on this | |
160 | <a href="analysis.htm">analysis</a> of Boost.Polygon performance we can expect | |
161 | this methodology to easily scale up to million gate blocks on standard | |
162 | workstations and arbitrarily large designs given sufficient system memory. | |
163 | let's start | |
164 | by implementing some data structures for our application.</p> | |
165 | <p><font face="Courier New" size="2">struct layout_rectangle {<br> | |
166 | int xl, yl, xh, yh;<br> | |
167 | std::string layer;<br> | |
168 | };</font></p> | |
169 | <p>Our layout rectangle is nice and minimal, just enough to store its data. | |
170 | It is defined in <a href="tutorial/layout_rectangle.hpp">layout_rectangle.hpp</a>. Next | |
171 | let's implement the layout | |
172 | pin in a similar way.</p> | |
173 | <p><font face="Courier New" size="2">struct layout_pin {<br> | |
174 | int xl, yl, xh, yh;<br> | |
175 | std::string layer;<br> | |
176 | std::string net;<br> | |
177 | };</font></p> | |
178 | <p>Our layout pin is defined in <a href="tutorial/layout_pin.hpp">layout_pin.hpp</a>. Now | |
179 | let's define a layout database object and populate it from our parsed | |
180 | layout data in in <a href="tutorial/layout_database.hpp">layout_database.hpp</a>.</p> | |
181 | <p><font face="Courier New" size="2">typedef std::map<std::string, | |
182 | boost::polygon::polygon_90_set_data<int> > layout_database;<br> | |
183 | <br> | |
184 | //map the layout rectangle data type to the boost::polygon::rectangle_concept<br> | |
185 | namespace boost { namespace polygon{<br> | |
186 | template <><br> | |
187 | struct rectangle_traits<layout_rectangle> {<br> | |
188 | typedef int coordinate_type;<br> | |
189 | typedef interval_data<int> interval_type;<br> | |
190 | static inline interval_type get(const layout_rectangle& | |
191 | rectangle, orientation_2d orient) {<br> | |
192 | if(orient == HORIZONTAL)<br> | |
193 | return interval_type(rectangle.xl, | |
194 | rectangle.xh);<br> | |
195 | return interval_type(rectangle.yl, rectangle.yh);<br> | |
196 | }<br> | |
197 | };<br> | |
198 | <br> | |
199 | template <><br> | |
200 | struct geometry_concept<layout_rectangle> { typedef rectangle_concept | |
201 | type; };<br> | |
202 | }}<br> | |
203 | <br> | |
204 | //insert layout rectangles into a layout database<br> | |
205 | inline void populate_layout_database(layout_database& layout, std::vector<layout_rectangle>& | |
206 | rects) {<br> | |
207 | for(std::size_t i = 0; i < rects.size(); ++i) {<br> | |
208 | layout[rects[i].layer].insert(rects[i]);<br> | |
209 | }<br> | |
210 | }</font></p> | |
211 | <p>We don't need to insert pins into the layout database because it doesn't know | |
212 | anything about connectivity, just geometry. However, we do need to know | |
213 | something about connectivity to compare a schematic to a layout, so we need to | |
214 | define our connectivity database and some logical objects. First we define | |
215 | an object for a logical device in <a href="tutorial/device.hpp">device.hpp</a>. | |
216 | Since we are lazy this object does double duty as a pin and both types of | |
217 | transistor. A traditional object oriented design might declare a base | |
218 | class with virtual destructor and derive every device from that. Since we | |
219 | aren't paid by the line of code let's just keep things simple.</p> | |
220 | <p><font face="Courier New" size="2">struct device {<br> | |
221 | std::string type;<br> | |
222 | std::vector<std::string> terminals;<br> | |
223 | };</font></p> | |
224 | <p>Now let's define a schematic database object in | |
225 | <a href="tutorial/schematic_database.hpp">schematic_database.hpp</a> and populate it from our parsed | |
226 | schematic data.</p> | |
227 | <p><font face="Courier New"><font size="2">struct schematic_database{<br> | |
228 | std::vector<device> devices;<br> | |
229 | std::map<std::string, std::set<std::size_t> > nets;<br> | |
230 | };<br> | |
231 | <br> | |
232 | //given a vector of devices populate the map of net name to set of device index<br> | |
233 | inline void extract_netlist(std::map<std::string, std::set<std::size_t> >& nets,<br> | |
234 | | |
235 | std::vector<device>& devices) {<br> | |
236 | for(std::size_t i = 0; i < devices.size(); ++i) {<br> | |
237 | for(std::size_t j = 0; j < devices[i].terminals.size(); ++j) | |
238 | {<br> | |
239 | //create association between net name and device | |
240 | id<br> | |
241 | | |
242 | nets[devices[i].terminals[j]].insert(nets[devices[i].terminals[j]].end(), i);<br> | |
243 | }<br> | |
244 | }<br> | |
245 | }</font></font></p> | |
246 | <p>Our schematic database is just a vector of devices, which are associated to | |
247 | nets by name through their terminals and a map of net name to set of device | |
248 | index into the vector, which completes the graph by associating nets with their | |
249 | devices. Given the devices and their terminal nets we easily build the | |
250 | mapping from nets to devices with the extract_netlist operation. Now we | |
251 | are ready to start working on extracting our layout to a derived schematic | |
252 | database. However, first we need to build a physical connectivity database | |
253 | with geometry in it before we can build a logical connectivity database from the | |
254 | layout. We define a simple connectivity database in | |
255 | <a href="tutorial/connectivity_database.hpp">connectivity_database.hpp</a> as a | |
256 | map of net name to layout database of geometry connected to that net and | |
257 | populate it with the layout database and pin data.</p> | |
258 | <p><font size="2" face="Courier New">typedef std::map<std::string, | |
259 | layout_database > connectivity_database;<br> | |
260 | <br> | |
261 | //map layout pin data type to boost::polygon::rectangle_concept<br> | |
262 | namespace boost { namespace polygon{<br> | |
263 | template <><br> | |
264 | struct rectangle_traits<layout_pin> {<br> | |
265 | typedef int coordinate_type;<br> | |
266 | typedef interval_data<int> interval_type;<br> | |
267 | static inline interval_type get(const layout_pin& pin, | |
268 | orientation_2d orient) {<br> | |
269 | if(orient == HORIZONTAL)<br> | |
270 | return interval_type(pin.xl, pin.xh);<br> | |
271 | return interval_type(pin.yl, pin.yh);<br> | |
272 | }<br> | |
273 | };<br> | |
274 | <br> | |
275 | template <><br> | |
276 | struct geometry_concept<layout_pin> { typedef rectangle_concept type; };<br> | |
277 | }}</font></p> | |
278 | <p><font size="2" face="Courier New">//given a layout_database we populate a | |
279 | connectivity database<br> | |
280 | inline void populate_connectivity_database(connectivity_database& connectivity,<br> | |
281 | | |
282 | std::vector<layout_pin>& pins, layout_database& layout) {<br> | |
283 | using namespace boost::polygon;<br> | |
284 | using namespace boost::polygon::operators;<br> | |
285 | for(std::size_t i = 0; i < pins.size(); ++i) {<br> | |
286 | connectivity[pins[i].net][pins[i].layer].insert(pins[i]);<br> | |
287 | }<br> | |
288 | int internal_net_suffix = 0;<br> | |
289 | //connect metal1 layout to pins which were on metal1<br> | |
290 | connect_layout_to_layer(connectivity, layout["METAL1"], "METAL1", <br> | |
291 | | |
292 | "METAL1", "__internal_net_", internal_net_suffix);<br> | |
293 | //connect via0 layout to metal1<br> | |
294 | connect_layout_to_layer(connectivity, layout["VIA0"], "VIA0", <br> | |
295 | | |
296 | "METAL1", "__internal_net_", internal_net_suffix);<br> | |
297 | //poly needs to have gates subtracted from it to prevent shorting through | |
298 | transistors<br> | |
299 | polygon_set poly_not_gate = layout["POLY"] - layout["GATE"];<br> | |
300 | //connect poly minus gate to via0<br> | |
301 | connect_layout_to_layer(connectivity, poly_not_gate, "POLY", <br> | |
302 | | |
303 | "VIA0", "__internal_net_", internal_net_suffix);<br> | |
304 | //we don't want to short signals through transistors so we subtract the | |
305 | gate regions<br> | |
306 | //from the diffusions<br> | |
307 | polygon_set diff_not_gate = (layout["PDIFF"] + layout["NDIFF"]) - | |
308 | layout["GATE"];<br> | |
309 | //connect diffusion minus gate to poly<br> | |
310 | //Note that I made up the DIFF layer name for combined P and NDIFF<br> | |
311 | connect_layout_to_layer(connectivity, diff_not_gate, "DIFF", <br> | |
312 | | |
313 | "POLY", "__internal_net_", internal_net_suffix);<br> | |
314 | //connect gate to poly to make connections through gates on poly<br> | |
315 | connect_layout_to_layer(connectivity, layout["GATE"], "GATE", <br> | |
316 | | |
317 | "POLY", "__internal_net_", internal_net_suffix);<br> | |
318 | //now we have traced connectivity of the layout down to the transistor | |
319 | level<br> | |
320 | //any polygons not connected to pins have been assigned internal net | |
321 | names<br> | |
322 | }</font></p> | |
323 | <p>This populate connectivity database function is our first real use of | |
324 | Boost.Polygon in our application. Here we are doing Boolean (polygon set) | |
325 | operations on layout layers to merge together the PDIFF and NDIFF layers and cut | |
326 | away the GATE layer from the result, for example. We connect up the layout | |
327 | starting from the pins and working our way down the layer stack to the | |
328 | transistor level. It would work equally well to work our way up the layer | |
329 | stack, or connect things up in any order, really, but this way produces fewer | |
330 | internal temporary nets that need to be merged when connections between them are | |
331 | discovered later. The connect layout to layer function used above needs to | |
332 | be implemented before we can populate our connectivity database.</p> | |
333 | <p><font size="2" face="Courier New">inline void | |
334 | connect_layout_to_layer(connectivity_database& connectivity, polygon_set& | |
335 | layout, <br> | |
336 | | |
337 | std::string layout_layer, std::string layer,<br> | |
338 | | |
339 | std::string net_prefix, int& net_suffix) {<br> | |
340 | if(layout_layer.empty())<br> | |
341 | return;<br> | |
342 | boost::polygon::connectivity_extraction_90<int> ce;<br> | |
343 | std::vector<std::string> net_ids;<br> | |
344 | for(connectivity_database::iterator itr = connectivity.begin(); itr != | |
345 | connectivity.end(); ++itr) {<br> | |
346 | net_ids.push_back((*itr).first);<br> | |
347 | ce.insert((*itr).second[layer]);<br> | |
348 | }<br> | |
349 | std::vector<polygon> polygons;<br> | |
350 | layout.get_polygons(polygons);<br> | |
351 | std::size_t polygon_id_offset = net_ids.size();<br> | |
352 | for(std::size_t i = 0; i < polygons.size(); ++i) {<br> | |
353 | ce.insert(polygons[i]);<br> | |
354 | }<br> | |
355 | std::vector<std::set<int> > graph(polygons.size() + net_ids.size(), | |
356 | std::set<int>());<br> | |
357 | ce.extract(graph);<br> | |
358 | std::vector<int> polygon_color(polygons.size() + net_ids.size(), 0);<br> | |
359 | //for each net in net_ids populate connected component with net<br> | |
360 | for(std::size_t node_id = 0; node_id < net_ids.size(); ++node_id) {<br> | |
361 | populate_connected_component(connectivity, polygons, | |
362 | polygon_color, graph, node_id, <br> | |
363 | polygon_id_offset, net_ids[node_id], net_ids, <br> | |
364 | net_prefix, layout_layer);<br> | |
365 | }<br> | |
366 | //for each polygon_color that is zero populate connected component with | |
367 | net_prefix + net_suffix++<br> | |
368 | for(std::size_t i = 0; i < polygons.size(); ++i) {<br> | |
369 | if(polygon_color[i + polygon_id_offset] == 0) {<br> | |
370 | std::stringstream ss(std::stringstream::in | | |
371 | std::stringstream::out);<br> | |
372 | ss << net_prefix << net_suffix++;<br> | |
373 | std::string internal_net; <br> | |
374 | ss >> internal_net;<br> | |
375 | populate_connected_component(connectivity, | |
376 | polygons, polygon_color, graph, <br> | |
377 | i + polygon_id_offset, <br> | |
378 | polygon_id_offset, internal_net, net_ids, <br> | |
379 | net_prefix, layout_layer);<br> | |
380 | }<br> | |
381 | }<br> | |
382 | }</font></p> | |
383 | <p>The connect layout to layer function uses the connectivity extraction feature | |
384 | of Boost.Polyon to build a connectivity graph for polygons on the input polygon | |
385 | set and in the connectivity database on the specified layer. It then finds | |
386 | polygons associated with existing nets in the connectivity database through | |
387 | graph traversal and inserts them into the connectivity database. Finally, | |
388 | polygons that weren't connected to existing nets are inserted into the | |
389 | connectivity database on auto-generated internal net names. The insertion | |
390 | of a connected component into the connectivity database is handled by the | |
391 | recursive traversal of the connectivity graph that we implement next.</p> | |
392 | <p><font size="2" face="Courier New">inline void populate_connected_component<br> | |
393 | (connectivity_database& connectivity, std::vector<polygon>& polygons, <br> | |
394 | std::vector<int> polygon_color, std::vector<std::set<int> >& graph, <br> | |
395 | std::size_t node_id, std::size_t polygon_id_offset, std::string& net, <br> | |
396 | std::vector<std::string>& net_ids, std::string net_prefix,<br> | |
397 | std::string& layout_layer) {<br> | |
398 | if(polygon_color[node_id] == 1)<br> | |
399 | return;<br> | |
400 | polygon_color[node_id] = 1;<br> | |
401 | if(node_id < polygon_id_offset && net_ids[node_id] != net) {<br> | |
402 | //merge nets in connectivity database<br> | |
403 | //if one of the nets is internal net merge it into the other<br> | |
404 | std::string net1 = net_ids[node_id];<br> | |
405 | std::string net2 = net;<br> | |
406 | if(net.compare(0, net_prefix.length(), net_prefix) == 0) {<br> | |
407 | net = net1;<br> | |
408 | std::swap(net1, net2);<br> | |
409 | } else {<br> | |
410 | net_ids[node_id] = net;<br> | |
411 | }<br> | |
412 | connectivity_database::iterator itr = | |
413 | connectivity.find(net1);<br> | |
414 | if(itr != connectivity.end()) {<br> | |
415 | for(layout_database::iterator itr2 = (*itr).second.begin();<br> | |
416 | itr2 != (*itr).second.end(); | |
417 | ++itr2) {<br> | |
418 | connectivity[net2][(*itr2).first].insert((*itr2).second);<br> | |
419 | }<br> | |
420 | connectivity.erase(itr);<br> | |
421 | }<br> | |
422 | }<br> | |
423 | if(node_id >= polygon_id_offset)<br> | |
424 | connectivity[net][layout_layer].insert(polygons[node_id - | |
425 | polygon_id_offset]);<br> | |
426 | for(std::set<int>::iterator itr = graph[node_id].begin();<br> | |
427 | itr != graph[node_id].end(); ++itr) {<br> | |
428 | populate_connected_component(connectivity, polygons, | |
429 | polygon_color, graph, <br> | |
430 | *itr, polygon_id_offset, net, net_ids, net_prefix, | |
431 | layout_layer);<br> | |
432 | }<br> | |
433 | }<br> | |
434 | <br> | |
435 | </font>We want to merge internally generated nets into pin nets, which is the | |
436 | most complicated part of this simple procedure. Now that we have our | |
437 | connectivity database extracted from pins down to transistors we need to extract | |
438 | our transistors and establish the relationship between transistor terminals and | |
439 | nets in our connectivity database. First let's extract transistors with | |
440 | the functions defined in defined in <a href="tutorial/extract_devices.hpp"> | |
441 | extract_devices.hpp</a>.</p> | |
442 | <p><font size="2" face="Courier New">typedef boost::polygon::connectivity_extraction_90<int> | |
443 | connectivity_extraction;<br> | |
444 | inline std::vector<std::set<int> ><br> | |
445 | extract_layer(connectivity_extraction& ce, std::vector<std::string>& net_ids,<br> | |
446 | | |
447 | connectivity_database& connectivity, polygon_set& layout,<br> | |
448 | | |
449 | std::string layer) {<br> | |
450 | for(connectivity_database::iterator itr = connectivity.begin(); itr != | |
451 | connectivity.end(); ++itr) {<br> | |
452 | net_ids.push_back((*itr).first);<br> | |
453 | ce.insert((*itr).second[layer]);<br> | |
454 | }<br> | |
455 | std::vector<polygon> polygons;<br> | |
456 | layout.get_polygons(polygons);<br> | |
457 | for(std::size_t i = 0; i < polygons.size(); ++i) {<br> | |
458 | ce.insert(polygons[i]);<br> | |
459 | }<br> | |
460 | std::vector<std::set<int> > graph(polygons.size() + net_ids.size(), | |
461 | std::set<int>());<br> | |
462 | ce.extract(graph);<br> | |
463 | return graph;<br> | |
464 | }</font></p> | |
465 | <p>This extract layer algorithm constructs a connectivity graph between polygons | |
466 | in the input polygon set and polygons in the given layer of the connectivity | |
467 | database. It is used to form the association between transistors and their | |
468 | terminal nets in the function for extracting a specific transistor type.</p> | |
469 | <p><font size="2" face="Courier New">inline void extract_device_type(std::vector<device>& | |
470 | devices, connectivity_database& connectivity,<br> | |
471 | | |
472 | polygon_set& layout, std::string type) {<br> | |
473 | //recall that P and NDIFF were merged into one DIFF layer in the | |
474 | connectivity database<br> | |
475 | //find the two nets on the DIFF layer that interact with each transistor<br> | |
476 | //and then find the net on the poly layer that interacts with each | |
477 | transistor<br> | |
478 | boost::polygon::connectivity_extraction_90<int> cediff;<br> | |
479 | std::vector<std::string> net_ids_diff;<br> | |
480 | std::vector<std::set<int> > graph_diff =<br> | |
481 | extract_layer(cediff, net_ids_diff, connectivity, layout, | |
482 | "DIFF");<br> | |
483 | boost::polygon::connectivity_extraction_90<int> cepoly;<br> | |
484 | std::vector<std::string> net_ids_poly;<br> | |
485 | std::vector<std::set<int> > graph_poly =<br> | |
486 | extract_layer(cepoly, net_ids_poly, connectivity, layout, | |
487 | "POLY");<br> | |
488 | std::vector<device> tmp_devices(graph_diff.size() - net_ids_poly.size());<br> | |
489 | for(std::size_t i = net_ids_poly.size(); i < graph_diff.size(); ++i) {<br> | |
490 | tmp_devices[i - net_ids_diff.size()].type = type;<br> | |
491 | tmp_devices[i - net_ids_diff.size()].terminals = std::vector<std::string>(3, | |
492 | std::string());<br> | |
493 | std::size_t j = 0;<br> | |
494 | for(std::set<int>::iterator itr = graph_diff[i].begin();<br> | |
495 | itr != graph_diff[i].end(); ++itr, | |
496 | ++j) {<br> | |
497 | if(j == 0) {<br> | |
498 | tmp_devices[i - net_ids_diff.size()].terminals[0] | |
499 | = net_ids_diff[*itr];<br> | |
500 | } else if(j == 1) {<br> | |
501 | tmp_devices[i - net_ids_diff.size()].terminals[2] | |
502 | = net_ids_diff[*itr];<br> | |
503 | } else {<br> | |
504 | //error, too many diff connections<br> | |
505 | tmp_devices[i - net_ids_diff.size()].terminals | |
506 | = std::vector<std::string>(3, std::string());<br> | |
507 | }<br> | |
508 | }<br> | |
509 | j = 0;<br> | |
510 | for(std::set<int>::iterator itr = graph_poly[i].begin();<br> | |
511 | itr != graph_poly[i].end(); ++itr, | |
512 | ++j) {<br> | |
513 | if(j == 0) {<br> | |
514 | tmp_devices[i - net_ids_diff.size()].terminals[1] | |
515 | = net_ids_poly[*itr];<br> | |
516 | } else {<br> | |
517 | //error, too many poly connections<br> | |
518 | tmp_devices[i - net_ids_poly.size()].terminals | |
519 | = std::vector<std::string>(3, std::string());<br> | |
520 | }<br> | |
521 | }<br> | |
522 | }<br> | |
523 | <br> | |
524 | devices.insert(devices.end(), tmp_devices.begin(), tmp_devices.end());<br> | |
525 | }</font></p> | |
526 | <p>We append transistors onto the vector of devices with their terminals | |
527 | populated with net names extracted from the connectivity database. | |
528 | Transistors' terminals are connected through the POLY and DIFF layers where DIFF | |
529 | contains both PDIFF and NDIFF. The connection to POLY layer is the gate of | |
530 | the transistor while the connections to DIFF on either side of the channel of | |
531 | the transistor are the source and drain. We can use this to extract are p | |
532 | and n-type transistors. <font size="2" face="Courier New"><br> | |
533 | <br> | |
534 | //populates vector of devices based on connectivity and layout data<br> | |
535 | inline void extract_devices(std::vector<device>& devices, connectivity_database& | |
536 | connectivity,<br> | |
537 | | |
538 | layout_database& layout) {<br> | |
539 | using namespace boost::polygon::operators;<br> | |
540 | //p-type transistors are gate that interact with p diffusion and nwell<br> | |
541 | polygon_set ptransistors = layout["GATE"];<br> | |
542 | ptransistors.interact(layout["PDIFF"]);<br> | |
543 | ptransistors.interact(layout["NWELL"]);<br> | |
544 | //n-type transistors are gate that interact with n diffusion and not | |
545 | nwell<br> | |
546 | polygon_set ntransistors = layout["GATE"];<br> | |
547 | ntransistors.interact(layout["NDIFF"]);<br> | |
548 | polygon_set not_ntransistors = ntransistors;<br> | |
549 | not_ntransistors.interact(layout["NWELL"]);<br> | |
550 | ntransistors -= not_ntransistors;<br> | |
551 | extract_device_type(devices, connectivity, ptransistors, "PTRANS");<br> | |
552 | extract_device_type(devices, connectivity, ntransistors, "NTRANS");<br> | |
553 | }</font></p> | |
554 | <p>The extract devices procedure makes some more use of Boost.Polygon Boolean | |
555 | operations on the layout data when we exclude GATE material over NDIFF that | |
556 | isn't also over NWELL to extract our n-type transistors. We also are using | |
557 | the "interact" operation on polygon gets, which is implemented in terms of | |
558 | connectivity extraction and retains all polygons of a polygon set that touch or | |
559 | overlap polygons from another polygon set. Now that we have a vector of | |
560 | devices we can build a schematic database by calling the extract_netlist | |
561 | function. We can then compare the extracted schematic from the schematic | |
562 | read in from file with the functions defined in | |
563 | <a href="tutorial/compare_schematics.hpp">compare_schematics.hpp</a>. | |
564 | Since comparing two schematics has no geometric aspect we won't go into that | |
565 | procedure here in the tutorial and will skip to the integration of all these | |
566 | procedures in defined in <a href="tutorial/extract.cpp">extract.cpp</a> to build | |
567 | the layout to schematic comparison algorithm.</p> | |
568 | <p><font size="2" face="Courier New">bool compare_files(std::string layout_file, | |
569 | std::string schematic_file) {<br> | |
570 | std::ifstream sin(schematic_file.c_str());<br> | |
571 | std::ifstream lin(layout_file.c_str());<br> | |
572 | <br> | |
573 | std::vector<layout_rectangle> rects;<br> | |
574 | std::vector<layout_pin> pins;<br> | |
575 | parse_layout(rects, pins, lin);<br> | |
576 | <br> | |
577 | schematic_database reference_schematic;<br> | |
578 | parse_schematic_database(reference_schematic, sin);<br> | |
579 | <br> | |
580 | layout_database layout;<br> | |
581 | populate_layout_database(layout, rects);<br> | |
582 | <br> | |
583 | connectivity_database connectivity;<br> | |
584 | populate_connectivity_database(connectivity, pins, layout);<br> | |
585 | <br> | |
586 | schematic_database schematic;<br> | |
587 | std::vector<device>& devices = schematic.devices;<br> | |
588 | for(std::size_t i = 0; i < pins.size(); ++i) {<br> | |
589 | devices.push_back(device());<br> | |
590 | devices.back().type = "PIN";<br> | |
591 | devices.back().terminals.push_back(pins[i].net);<br> | |
592 | }<br> | |
593 | extract_devices(devices, connectivity, layout);<br> | |
594 | extract_netlist(schematic.nets, devices);<br> | |
595 | return compare_schematics(reference_schematic, schematic);<br> | |
596 | }</font></p> | |
597 | <p><font face="Courier New" size="2">int main(int argc, char **argv) {<br> | |
598 | if(argc < 3) {<br> | |
599 | std::cout << "usage: " << argv[0] << " <layout_file> <schematic_file>" | |
600 | << std::endl;<br> | |
601 | return -1;<br> | |
602 | }<br> | |
603 | bool result = compare_files(argv[1], argv[2]);<br> | |
604 | if(result == false) {<br> | |
605 | std::cout << "Layout does not match schematic." << std::endl;<br> | |
606 | return 1;<br> | |
607 | } <br> | |
608 | std::cout << "Layout does match schematic." << std::endl;<br> | |
609 | return 0;<br> | |
610 | }<br> | |
611 | <br> | |
612 | </font>We test the program with two schematics and three layouts. These | |
613 | include a nand and a nor gate layout and schematic as well as an incorrect nand | |
614 | gate layout. The nand layout and schematic are the same as shown above.<font face="Courier New" size="2"> | |
615 | </font></p> | |
616 | <p><font face="Courier New" size="2">> lvs<br> | |
617 | usage: lvs <layout_file> <schematic_file><br> | |
618 | > lvs nand.layout nand.schematic <br> | |
619 | Layout does match schematic.<br> | |
620 | > lvs nand_short.layout nand.schematic <br> | |
621 | Layout does not match schematic.<br> | |
622 | > lvs nand.layout nor.schematic <br> | |
623 | Layout does not match schematic.<br> | |
624 | > lvs nor.layout nor.schematic <br> | |
625 | Layout does match schematic.<br> | |
626 | > lvs nor.layout nand.schematic <br> | |
627 | Layout does not match schematic.</font></p> | |
628 | <p>This concludes our tutorial on how to build a simple layout to schematic | |
629 | verification application based on Boost.Polygon library capabilities. The | |
630 | implementation of this application made many simplifying assumptions that are | |
631 | not valid in the real world and hard coded a lot of things that need to be | |
632 | configurable in a real layout verification application. However, it does | |
633 | give an idea of how to use Boost.Polygon to solve real problems and points in | |
634 | the direction of how a real application might use Boost.Polygon.</p> | |
635 | ||
636 | ||
637 | <table class="docinfo" rules="none" frame="void" id="table1"> | |
638 | <colgroup> | |
639 | <col class="docinfo-name"><col class="docinfo-content"> | |
640 | </colgroup> | |
641 | <tbody vAlign="top"> | |
642 | <tr> | |
643 | <th class="docinfo-name">Copyright:</th> | |
644 |