From 1db5ad016c4e720793f19186c581b0c0713bc263 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 26 Dec 2018 00:39:46 +0100 Subject: [PATCH] WIP: added first tests for device extraction. --- .../dbNetlistDeviceExtractorTests.cc | 311 ++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 2 files changed, 313 insertions(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc index e69de29bb..cfd4b7a38 100644 --- a/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceExtractorTests.cc @@ -0,0 +1,311 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistDeviceExtractor.h" +#include "dbNetlistDeviceClasses.h" +#include "dbHierNetworkProcessor.h" +#include "dbLayout.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" +#include "dbStream.h" +#include "dbDeepRegion.h" +#include "dbDeepShapeStore.h" +#include "dbReader.h" +#include "dbWriter.h" +#include "dbCommonReader.h" + +#include "tlUnitTest.h" +#include "tlString.h" +#include "tlFileUtils.h" + +#include + +class MOSFETExtractor + : public db::NetlistDeviceExtractor +{ +public: + MOSFETExtractor (db::Layout *debug_out) + : db::NetlistDeviceExtractor (), mp_debug_out (debug_out), m_ldiff (0), m_lgate (0) + { + if (mp_debug_out) { + m_ldiff = mp_debug_out->insert_layer (db::LayerProperties (100, 0)); + m_lgate = mp_debug_out->insert_layer (db::LayerProperties (101, 0)); + } + } + + virtual void create_device_classes () + { + db::DeviceClassMOS3Transistor *pmos_class = new db::DeviceClassMOS3Transistor (); + pmos_class->set_name ("PMOS"); + register_device_class (pmos_class); + + db::DeviceClassMOS3Transistor *nmos_class = new db::DeviceClassMOS3Transistor (); + nmos_class->set_name ("NMOS"); + register_device_class (nmos_class); + } + + virtual db::Connectivity get_connectivity (const db::Layout & /*layout*/, const std::vector &layers) const + { + tl_assert (layers.size () == 3); + + unsigned int lpdiff = layers [0]; + unsigned int lndiff = layers [1]; + unsigned int gate = layers [2]; + + // The layer definition is pdiff, ndiff, gate + db::Connectivity conn; + // collect all connected pdiff + conn.connect (lpdiff, lpdiff); + // collect all connected ndiff + conn.connect (lndiff, lndiff); + // collect all connected gate shapes + conn.connect (gate, gate); + // connect gate with pdiff + conn.connect (lpdiff, gate); + // connect gate with ndiff + conn.connect (lndiff, gate); + return conn; + } + + virtual void extract_devices (const std::vector &layer_geometry) + { + const db::Region &rpdiff = layer_geometry [0]; + const db::Region &rndiff = layer_geometry [1]; + const db::Region &rgates = layer_geometry [2]; + + for (db::Region::const_iterator p = rgates.begin_merged (); !p.at_end (); ++p) { + + db::Region rgate (*p); + db::Region rpdiff_on_gate = rpdiff.selected_interacting (rgate); + db::Region rndiff_on_gate = rndiff.selected_interacting (rgate); + + if (! rpdiff_on_gate.empty () && ! rndiff_on_gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches both ndiff and pdiff - ignored")), *p); + } else if (rpdiff_on_gate.empty () && rndiff_on_gate.empty ()) { + error (tl::to_string (tr ("Gate shape touches neither ndiff and pdiff - ignored")), *p); + } else { + + db::Region &diff = (rpdiff_on_gate.empty () ? rndiff_on_gate : rpdiff_on_gate); + + if (diff.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two polygons on diff interacting one gate shape (found %d) - gate shape ignored")), int (diff.size ())), *p); + continue; + } + + db::Edges edges (rgate.edges () & diff.edges ()); + if (edges.size () != 2) { + error (tl::sprintf (tl::to_string (tr ("Expected two edges interacting gate/diff (found %d) - width and length may be incorrect")), int (edges.size ())), *p); + continue; + } + + if (! p->is_box ()) { + error (tl::to_string (tr ("Gate shape is not a box - width and length may be incorrect")), *p); + } + + db::Device *device = create_device (! rpdiff_on_gate.empty () ? 0 /*pdiff*/ : 1 /*ndiff*/); + + device->set_parameter_value ("W", dbu () * edges.length () * 0.5); + device->set_parameter_value ("L", dbu () * (p->perimeter () - edges.length ()) * 0.5); + + int index = 0; + for (db::Region::const_iterator d = diff.begin (); !d.at_end () && index < 2; ++d, ++index) { + + // count the number of gate shapes attached to this shape and distribute the area of the + // diffusion area to the number of gates + int n = rgates.selected_interacting (db::Region (*d)).size (); + tl_assert (n > 0); + + device->set_parameter_value (index == 0 ? "AS" : "AD", dbu () * dbu () * d->area () / double (n)); + + } + + // @@@ create terminals + + device_out (device, diff, rgate); + + } + + } + } + + void error (const std::string &msg) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg; + } + + void error (const std::string &msg, const db::Polygon &poly) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << poly.to_string () << ")"; + } + + void error (const std::string &msg, const db::Region ®ion) + { + // @@@ TODO: move this to device extractor + tl::error << tr ("Error in cell '") << cell_name () << "': " << msg << " (" << region.to_string () << ")"; + } + + std::string cell_name () const + { + // @@@ TODO: move this to device extractor + return layout ()->cell_name (cell_index ()); + } + +private: + db::Layout *mp_debug_out; + unsigned int m_ldiff, m_lgate; + + void device_out (const db::Device *device, const db::Region &diff, const db::Region &gate) + { + if (! mp_debug_out) { + return; + } + + std::string cn = layout ()->cell_name (cell_index ()); + std::pair target_cp = mp_debug_out->cell_by_name (cn.c_str ()); + tl_assert (target_cp.first); + + db::cell_index_type dci = mp_debug_out->add_cell ((device->device_class ()->name () + "_" + device->name ()).c_str ()); + mp_debug_out->cell (target_cp.second).insert (db::CellInstArray (db::CellInst (dci), db::Trans ())); + + db::Cell &device_cell = mp_debug_out->cell (dci); + for (db::Region::const_iterator p = diff.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_ldiff).insert (*p); + } + for (db::Region::const_iterator p = gate.begin (); ! p.at_end (); ++p) { + device_cell.shapes (m_lgate).insert (*p); + } + + std::string ps; + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (! ps.empty ()) { + ps += ","; + } + ps += i->name () + "=" + tl::to_string (device->parameter_value (i->id ())); + } + device_cell.shapes (m_ldiff).insert (db::Text (ps, db::Trans (diff.bbox ().center () - db::Point ()))); + } +}; + +TEST(1_DeviceNetExtraction) +{ + bool write_debug = true; + + db::Layout ly; + unsigned int nwell, active, poly; + + db::LayerProperties p; + db::LayerMap lmap; + + p.layer = 1; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), nwell = ly.insert_layer ()); + ly.set_properties (nwell, p); + + p.layer = 2; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), active = ly.insert_layer ()); + ly.set_properties (active, p); + + p.layer = 3; + p.datatype = 0; + lmap.map (db::LDPair (p.layer, p.datatype), poly = ly.insert_layer ()); + ly.set_properties (poly, p); + + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "device_extract_l1.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + + // derived regions + db::Region rgate = ractive & rpoly; + db::Region rsd = ractive - rgate; + db::Region rpdiff = rsd & rnwell; + db::Region rndiff = rsd - rnwell; + + if (write_debug) { + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + + rgate.insert_into (&ly, tc.cell_index (), lgate); + rsd.insert_into (&ly, tc.cell_index (), lsd); + rpdiff.insert_into (&ly, tc.cell_index (), lpdiff); + rndiff.insert_into (&ly, tc.cell_index (), lndiff); + + } + + db::DeepRegion *dr = dynamic_cast (rnwell.delegate ()); + db::DeepLayer dl = dr->deep_layer (); + dl.layout (); + dl.initial_cell (); + dl.layer (); + + db::Netlist nl; + + MOSFETExtractor ex (write_debug ? &ly : 0); + ex.initialize (&nl); + + std::vector region_ptrs; + region_ptrs.push_back (&rpdiff); + region_ptrs.push_back (&rndiff); + region_ptrs.push_back (&rgate); + ex.extract (region_ptrs); + + if (write_debug) { + + std::string fn (tl::testtmp ()); + fn = tl::combine_path (fn, "debug-1_DeviceNetExtraction.gds"); + + tl::OutputStream stream (fn); + db::SaveLayoutOptions options; + db::Writer writer (options); + writer.write (ly, stream); + + tl::log << "Device layer debug file written to: " << tl::absolute_file_path (fn); + + } +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 63a29945c..ac3cd3382 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -60,7 +60,8 @@ SOURCES = \ dbDeepShapeStoreTests.cc \ dbHierNetworkProcessorTests.cc \ dbNetlistPropertyTests.cc \ - dbNetlistTests.cc + dbNetlistTests.cc \ + dbNetlistDeviceExtractorTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC