mirror of https://github.com/KLayout/klayout.git
Allow empty regions for device extractor.
This commit is contained in:
parent
21ea37f747
commit
36a3540e16
|
|
@ -222,11 +222,20 @@ DeepLayer::check_dss () const
|
||||||
struct DeepShapeStore::LayoutHolder
|
struct DeepShapeStore::LayoutHolder
|
||||||
{
|
{
|
||||||
LayoutHolder (const db::ICplxTrans &trans)
|
LayoutHolder (const db::ICplxTrans &trans)
|
||||||
: refs (0), layout (false), builder (&layout, trans)
|
: refs (0), layout (false), builder (&layout, trans), m_empty_layer (std::numeric_limits<unsigned int>::max ())
|
||||||
{
|
{
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int make_empty_layer ()
|
||||||
|
{
|
||||||
|
if (m_empty_layer == std::numeric_limits<unsigned int>::max ()) {
|
||||||
|
m_empty_layer = layout.insert_layer ();
|
||||||
|
layer_refs [m_empty_layer] += 1; // the empty layer is not deleted
|
||||||
|
}
|
||||||
|
return m_empty_layer;
|
||||||
|
}
|
||||||
|
|
||||||
void add_layer_ref (unsigned int layer)
|
void add_layer_ref (unsigned int layer)
|
||||||
{
|
{
|
||||||
layer_refs [layer] += 1;
|
layer_refs [layer] += 1;
|
||||||
|
|
@ -244,6 +253,9 @@ struct DeepShapeStore::LayoutHolder
|
||||||
db::Layout layout;
|
db::Layout layout;
|
||||||
db::HierarchyBuilder builder;
|
db::HierarchyBuilder builder;
|
||||||
std::map<unsigned int, int> layer_refs;
|
std::map<unsigned int, int> layer_refs;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int m_empty_layer;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
|
@ -426,6 +438,17 @@ DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator
|
||||||
return DeepLayer (this, layout_index, layer_index);
|
return DeepLayer (this, layout_index, layer_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeepLayer DeepShapeStore::empty_layer (unsigned int layout_index)
|
||||||
|
{
|
||||||
|
return DeepLayer (this, layout_index, m_layouts[layout_index]->make_empty_layer ());
|
||||||
|
}
|
||||||
|
|
||||||
|
DeepLayer DeepShapeStore::empty_layer ()
|
||||||
|
{
|
||||||
|
require_singular ();
|
||||||
|
return empty_layer (0);
|
||||||
|
}
|
||||||
|
|
||||||
DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const db::ICplxTrans &trans)
|
DeepLayer DeepShapeStore::create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const db::ICplxTrans &trans)
|
||||||
{
|
{
|
||||||
unsigned int layout_index = layout_for_iter (si, trans);
|
unsigned int layout_index = layout_for_iter (si, trans);
|
||||||
|
|
|
||||||
|
|
@ -294,6 +294,19 @@ public:
|
||||||
*/
|
*/
|
||||||
DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ());
|
DeepLayer create_custom_layer (const db::RecursiveShapeIterator &si, HierarchyBuilderShapeReceiver *pipe, const ICplxTrans &trans = db::ICplxTrans ());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the empty working layer
|
||||||
|
*
|
||||||
|
* This method will deliver an empty layer for the given layout index. CAUTION: don't modify this layer as it may
|
||||||
|
* be reused.
|
||||||
|
*/
|
||||||
|
DeepLayer empty_layer (unsigned int layout_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the empty working layer for the singular layout
|
||||||
|
*/
|
||||||
|
DeepLayer empty_layer ();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Inserts the deep layer's shapes into some target layout
|
* @brief Inserts the deep layer's shapes into some target layout
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -140,9 +140,16 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi
|
||||||
tl_assert (l->second != 0);
|
tl_assert (l->second != 0);
|
||||||
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (l->second->delegate ());
|
db::DeepRegion *dr = dynamic_cast<db::DeepRegion *> (l->second->delegate ());
|
||||||
if (dr == 0) {
|
if (dr == 0) {
|
||||||
|
|
||||||
|
if (l->second->empty ()) {
|
||||||
|
// provide a substitute empty layer
|
||||||
|
layers.push_back (dss.empty_layer ().layer ());
|
||||||
|
} else {
|
||||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name));
|
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: must be of deep region kind")), ld->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
if (&dr->deep_layer ().layout () != &dss.layout () || &dr->deep_layer ().initial_cell () != &dss.initial_cell ()) {
|
if (&dr->deep_layer ().layout () != &dss.layout () || &dr->deep_layer ().initial_cell () != &dss.initial_cell ()) {
|
||||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name));
|
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction: not originating from the same source")), ld->name));
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +158,8 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, const NetlistDevi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extract_without_initialize (dss.layout (), dss.initial_cell (), clusters, layers);
|
extract_without_initialize (dss.layout (), dss.initial_cell (), clusters, layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1742,3 +1742,158 @@ TEST(6_MoreDeviceTypes)
|
||||||
" DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n"
|
" DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(7_MoreByEmptyDeviceTypes)
|
||||||
|
{
|
||||||
|
db::Layout ly;
|
||||||
|
db::LayerMap lmap;
|
||||||
|
|
||||||
|
unsigned int nwell = define_layer (ly, lmap, 1);
|
||||||
|
unsigned int active = define_layer (ly, lmap, 2);
|
||||||
|
unsigned int thickgox = define_layer (ly, lmap, 1003); // does not exist
|
||||||
|
unsigned int pplus = define_layer (ly, lmap, 4);
|
||||||
|
unsigned int nplus = define_layer (ly, lmap, 5);
|
||||||
|
unsigned int poly = define_layer (ly, lmap, 6);
|
||||||
|
unsigned int poly_lbl = define_layer (ly, lmap, 7);
|
||||||
|
unsigned int cont = define_layer (ly, lmap, 8);
|
||||||
|
unsigned int metal1 = define_layer (ly, lmap, 9);
|
||||||
|
unsigned int metal1_lbl = define_layer (ly, lmap, 10);
|
||||||
|
unsigned int via1 = define_layer (ly, lmap, 11);
|
||||||
|
unsigned int metal2 = define_layer (ly, lmap, 12);
|
||||||
|
unsigned int metal2_lbl = define_layer (ly, lmap, 13);
|
||||||
|
|
||||||
|
{
|
||||||
|
db::LoadLayoutOptions options;
|
||||||
|
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
|
||||||
|
options.get_options<db::CommonReaderOptions> ().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_l6.gds");
|
||||||
|
|
||||||
|
tl::InputStream stream (fn);
|
||||||
|
db::Reader reader (stream);
|
||||||
|
reader.read (ly, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
db::Cell &tc = ly.cell (*ly.begin_top_down ());
|
||||||
|
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
|
||||||
|
|
||||||
|
std::auto_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
|
||||||
|
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
|
||||||
|
std::auto_ptr<db::Region> rthickgox (l2n.make_layer (thickgox, "thickgox"));
|
||||||
|
std::auto_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
|
||||||
|
std::auto_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
|
||||||
|
std::auto_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
|
||||||
|
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
|
||||||
|
std::auto_ptr<db::Region> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
|
||||||
|
std::auto_ptr<db::Region> rcont (l2n.make_polygon_layer (cont, "cont"));
|
||||||
|
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
|
||||||
|
std::auto_ptr<db::Region> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
|
||||||
|
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
|
||||||
|
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
|
||||||
|
std::auto_ptr<db::Region> rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl"));
|
||||||
|
|
||||||
|
// derived regions
|
||||||
|
|
||||||
|
db::Region ractive_in_nwell = *ractive & *rnwell;
|
||||||
|
db::Region rpactive = ractive_in_nwell - *rnplus;
|
||||||
|
db::Region rntie = ractive_in_nwell & *rnplus;
|
||||||
|
db::Region rpgate = rpactive & *rpoly;
|
||||||
|
db::Region rpsd = rpactive - rpgate;
|
||||||
|
db::Region rhv_pgate = rpgate & *rthickgox;
|
||||||
|
db::Region rlv_pgate = rpgate - rhv_pgate;
|
||||||
|
db::Region rhv_psd = rpsd & *rthickgox;
|
||||||
|
db::Region rlv_psd = rpsd - *rthickgox;
|
||||||
|
|
||||||
|
l2n.register_layer(rntie, "ntie");
|
||||||
|
l2n.register_layer(rpsd, "psd");
|
||||||
|
|
||||||
|
db::Region ractive_outside_nwell = *ractive - *rnwell;
|
||||||
|
db::Region rnactive = ractive_outside_nwell - *rpplus;
|
||||||
|
db::Region rptie = ractive_outside_nwell & *rpplus;
|
||||||
|
db::Region rngate = rnactive & *rpoly;
|
||||||
|
db::Region rnsd = rnactive - rngate;
|
||||||
|
db::Region rhv_ngate = rngate & *rthickgox;
|
||||||
|
db::Region rlv_ngate = rngate - rhv_ngate;
|
||||||
|
db::Region rhv_nsd = rnsd & *rthickgox;
|
||||||
|
db::Region rlv_nsd = rnsd - *rthickgox;
|
||||||
|
|
||||||
|
l2n.register_layer(rptie, "ptie");
|
||||||
|
l2n.register_layer(rnsd, "nsd");
|
||||||
|
|
||||||
|
db::NetlistDeviceExtractorMOS4Transistor hvpmos_ex ("HVPMOS");
|
||||||
|
db::NetlistDeviceExtractorMOS4Transistor hvnmos_ex ("HVNMOS");
|
||||||
|
db::NetlistDeviceExtractorMOS4Transistor lvpmos_ex ("LVPMOS");
|
||||||
|
db::NetlistDeviceExtractorMOS4Transistor lvnmos_ex ("LVNMOS");
|
||||||
|
|
||||||
|
// device extraction
|
||||||
|
|
||||||
|
db::NetlistDeviceExtractor::input_layers dl;
|
||||||
|
|
||||||
|
dl["SD"] = &rpsd;
|
||||||
|
dl["G"] = &rhv_pgate;
|
||||||
|
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||||
|
dl["W"] = rnwell.get ();
|
||||||
|
l2n.extract_devices (hvpmos_ex, dl);
|
||||||
|
|
||||||
|
dl["SD"] = &rpsd;
|
||||||
|
dl["G"] = &rlv_pgate;
|
||||||
|
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||||
|
dl["W"] = rnwell.get ();
|
||||||
|
l2n.extract_devices (lvpmos_ex, dl);
|
||||||
|
|
||||||
|
dl["SD"] = &rnsd;
|
||||||
|
dl["G"] = &rhv_ngate;
|
||||||
|
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||||
|
dl["W"] = rbulk.get ();
|
||||||
|
l2n.extract_devices (hvnmos_ex, dl);
|
||||||
|
|
||||||
|
dl["SD"] = &rnsd;
|
||||||
|
dl["G"] = &rlv_ngate;
|
||||||
|
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
|
||||||
|
dl["W"] = rbulk.get ();
|
||||||
|
l2n.extract_devices (lvnmos_ex, dl);
|
||||||
|
|
||||||
|
// Intra-layer
|
||||||
|
l2n.connect (rpsd);
|
||||||
|
l2n.connect (rnsd);
|
||||||
|
l2n.connect (*rnwell);
|
||||||
|
l2n.connect (*rpoly);
|
||||||
|
l2n.connect (*rcont);
|
||||||
|
l2n.connect (*rmetal1);
|
||||||
|
l2n.connect (*rvia1);
|
||||||
|
l2n.connect (*rmetal2);
|
||||||
|
l2n.connect (rptie);
|
||||||
|
l2n.connect (rntie);
|
||||||
|
// Inter-layer
|
||||||
|
l2n.connect (*rcont, rntie);
|
||||||
|
l2n.connect (*rcont, rptie);
|
||||||
|
l2n.connect (*rnwell, rntie);
|
||||||
|
l2n.connect (rpsd, *rcont);
|
||||||
|
l2n.connect (rnsd, *rcont);
|
||||||
|
l2n.connect (*rpoly, *rcont);
|
||||||
|
l2n.connect (*rcont, *rmetal1);
|
||||||
|
l2n.connect (*rmetal1, *rvia1);
|
||||||
|
l2n.connect (*rvia1, *rmetal2);
|
||||||
|
l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
|
||||||
|
l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
|
||||||
|
l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
|
||||||
|
// Global
|
||||||
|
l2n.connect_global (rptie, "BULK");
|
||||||
|
l2n.connect_global (*rbulk, "BULK");
|
||||||
|
|
||||||
|
l2n.extract_netlist ();
|
||||||
|
|
||||||
|
// compare netlist as string
|
||||||
|
EXPECT_EQ (l2n.netlist ()->to_string (),
|
||||||
|
"Circuit TOP ():\n"
|
||||||
|
" DLVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n"
|
||||||
|
" DLVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n"
|
||||||
|
" DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n"
|
||||||
|
" DLVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n"
|
||||||
|
" DLVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n"
|
||||||
|
" DLVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue