klayout/src/db/unit_tests/dbLibraries.cc

620 lines
20 KiB
C++

/*
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 "dbLayout.h"
#include "dbPCellHeader.h"
#include "dbPCellDeclaration.h"
#include "dbPCellVariant.h"
#include "dbLibrary.h"
#include "dbLibraryManager.h"
#include "dbLibraryProxy.h"
#include "dbWriter.h"
#include "dbReader.h"
#include "dbLayoutDiff.h"
#include "tlStream.h"
#include "tlStaticObjects.h"
#include "tlUnitTest.h"
#include <memory>
class LIBT_PD
: public db::PCellDeclaration
{
virtual std::vector<db::PCellLayerDeclaration> get_layer_declarations (const db::pcell_parameters_type &) const
{
std::vector<db::PCellLayerDeclaration> layers;
layers.push_back(db::PCellLayerDeclaration ());
layers.back ().symbolic = "gate";
layers.back ().layer = 16;
layers.back ().datatype = 0;
layers.push_back(db::PCellLayerDeclaration ());
layers.back ().symbolic = "metal0";
layers.back ().layer = 24;
layers.back ().datatype = 0;
layers.push_back(db::PCellLayerDeclaration ());
layers.back ().symbolic = "cont";
layers.back ().layer = 23;
layers.back ().datatype = 0;
return layers;
}
virtual std::vector<db::PCellParameterDeclaration> get_parameter_declarations () const
{
std::vector<db::PCellParameterDeclaration> parameters;
parameters.push_back (db::PCellParameterDeclaration ("length"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.push_back (db::PCellParameterDeclaration ("width"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_double);
parameters.push_back (db::PCellParameterDeclaration ("orientation"));
parameters.back ().set_type (db::PCellParameterDeclaration::t_int);
return parameters;
}
virtual void produce (const db::Layout &layout, const std::vector<unsigned int> &layer_ids, const db::pcell_parameters_type &parameters, db::Cell &cell) const
{
db::Coord width = db::coord_traits<db::Coord>::rounded (parameters[0].to_double () / layout.dbu ());
db::Coord height = db::coord_traits<db::Coord>::rounded (parameters[1].to_double () / layout.dbu ());
int orientation = parameters[2].to_long ();
//unsigned int l_gate = layer_ids[0];
unsigned int l_metal0 = layer_ids[1];
//unsigned int l_cont = layer_ids[2];
const db::Cell &cell_a = layout.cell (layout.cell_by_name ("A").second);
cell.insert (db::CellInstArray (db::CellInst (cell_a.cell_index ()), db::Trans (orientation, db::Vector (width / 2 - 50, height / 2 - 100))));
cell.shapes (l_metal0).insert (db::Box (0, 0, width, height));
}
};
class LIBT_L
: public db::Library
{
public:
LIBT_L (tl::TestBase *_this)
: Library ()
{
set_name("L");
set_description("A test library.");
layout ().dbu (0.001);
db::LayerProperties p;
p.layer = 23;
p.datatype = 0;
unsigned int l_cont = layout ().insert_layer (p);
p.layer = 16;
p.datatype = 0;
unsigned int l_gate = layout ().insert_layer (p);
db::Cell &cell_a = layout ().cell (layout ().add_cell ("A"));
cell_a.shapes(l_cont).insert(db::Box (50, 50, 150, 150));
cell_a.shapes(l_gate).insert(db::Box (0, 0, 200, 1000));
db::Cell &top = layout ().cell (layout ().add_cell ("TOP"));
db::pcell_id_type pd = layout ().register_pcell ("PD", new LIBT_PD ());
std::vector<tl::Variant> parameters;
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
tl::Variant &width = parameters[0];
tl::Variant &height = parameters[1];
tl::Variant &orientation = parameters[2];
width = 0.5;
height = 1.0;
orientation = long (0);
db::cell_index_type pd1 = layout ().get_pcell_variant (pd, parameters);
db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (pd1), db::Trans (db::Vector (0, 0))));
width = width.to_double () * 0.1;
width = width.to_double () * 10.0;
db::cell_index_type pd2 = layout ().get_pcell_variant (pd, parameters);
db::Instance i2 = top.insert (db::CellInstArray (db::CellInst (pd2), db::Trans (db::Vector (0, 2000))));
EXPECT_EQ (pd1, pd2);
width = 0.4;
height = 0.8;
orientation = long (1);
db::cell_index_type pd3 = layout ().get_pcell_variant (pd, parameters);
db::Instance i3 = top.insert (db::CellInstArray (db::CellInst (pd3), db::Trans (db::Vector (2000, 0))));
}
~LIBT_L()
{
// .. nothing yet ..
}
};
class LIBT_A
: public db::Library
{
public:
LIBT_A ()
: Library ()
{
set_name("A");
layout ().dbu (0.001);
db::LayerProperties p;
p.layer = 1;
p.datatype = 0;
unsigned int l1 = layout ().insert_layer (p);
p.layer = 2;
p.datatype = 0;
unsigned int l2 = layout ().insert_layer (p);
db::Cell &cell_a = layout ().cell (layout ().add_cell ("A"));
cell_a.shapes(l1).insert(db::Box (50, 50, 150, 150));
cell_a.shapes(l2).insert(db::Box (0, 0, 200, 1000));
}
};
class LIBT_B
: public db::Library
{
public:
LIBT_B ()
: Library ()
{
set_name("B");
layout ().dbu (0.001);
db::LayerProperties p;
p.layer = 1;
p.datatype = 0;
unsigned int l1 = layout ().insert_layer (p);
p.layer = 3;
p.datatype = 0;
unsigned int l3 = layout ().insert_layer (p);
db::Cell &cell_b = layout ().cell (layout ().add_cell ("B"));
cell_b.shapes(l1).insert(db::Box (10, 20, 30, 40));
cell_b.shapes(l3).insert(db::Box (0, 0, 10, 20));
db::Library *lib_a = db::LibraryManager::instance ().lib_ptr_by_name ("A");
tl_assert (lib_a != 0);
std::pair<bool, db::cell_index_type> a = lib_a->layout ().cell_by_name ("A");
tl_assert (a.first);
db::cell_index_type cp = layout ().get_lib_proxy (lib_a, a.second);
cell_b.insert (db::CellInstArray (db::CellInst (cp), db::ICplxTrans (0.1, 0.0, false, db::Vector (1.0, 2.0))));
}
};
static bool compare_vs_au (const tl::TestBase *tb, const db::Layout &layout, const std::string &filename)
{
db::Layout layout_au;
std::string fn (tl::testsrc ());
fn += "/testdata/gds/";
fn += filename;
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (layout_au);
// generate a "unique" name ...
unsigned int hash = 0;
for (const char *cp = filename.c_str (); *cp; ++cp) {
hash = (hash << 4) ^ (hash >> 4) ^ ((unsigned int) *cp);
}
std::string tmp_file = tb->tmp_file (tl::sprintf ("tmp_%x.gds", hash));
// "normalize" the layout by writing and reading ...
{
db::Writer writer = db::Writer (db::SaveLayoutOptions ());
tl::OutputStream stream (tmp_file);
writer.write (const_cast<db::Layout &> (layout), stream);
}
db::Layout tmp;
tl::InputStream tmp_stream (tmp_file);
db::Reader reader_tmp (tmp_stream);
reader_tmp.read (tmp);
bool equal = db::compare_layouts (tmp, layout_au, db::layout_diff::f_verbose, 0);
if (! equal) {
tl::warn << tl::sprintf ("Compare failed - see %s vs %s\n", tmp_file, fn);
}
return equal;
}
TEST(1)
{
bool equal;
std::vector<std::string> libnames_before;
for (db::LibraryManager::iterator il = db::LibraryManager::instance ().begin (); il != db::LibraryManager::instance ().end (); ++il) {
libnames_before.push_back (il->first);
}
std::sort (libnames_before.begin (), libnames_before.end ());
LIBT_L *l = new LIBT_L (_this);
db::lib_id_type lib_id = db::LibraryManager::instance ().register_lib (l);
try {
std::vector<std::string> libnames_withl;
for (db::LibraryManager::iterator il = db::LibraryManager::instance ().begin (); il != db::LibraryManager::instance ().end (); ++il) {
libnames_withl.push_back (il->first);
}
std::sort (libnames_withl.begin (), libnames_withl.end ());
std::vector<std::string> ll = libnames_before;
ll.push_back ("L");
std::sort (ll.begin (), ll.end ());
EXPECT_EQ (tl::join (libnames_withl, ","), tl::join (ll, ","));
std::pair<bool, db::lib_id_type> lbn;
lbn = db::LibraryManager::instance ().lib_by_name ("X");
EXPECT_EQ (lbn.first, false);
lbn = db::LibraryManager::instance ().lib_by_name ("L");
EXPECT_EQ (lbn.first, true);
EXPECT_EQ (lbn.second, lib_id);
db::Library *lib = db::LibraryManager::instance ().lib (lib_id);
EXPECT_EQ (lib == l, true);
EXPECT_EQ (lib->get_id (), lib_id);
EXPECT_EQ (lib->get_name (), "L");
EXPECT_EQ (lib->get_description (), "A test library.");
EXPECT_EQ (lib->layout ().get_properties(0).to_string (), "23/0");
EXPECT_EQ (lib->layout ().get_properties(1).to_string (), "16/0");
EXPECT_EQ (lib->layout ().get_properties(2).to_string (), "24/0");
db::Manager m;
db::Layout layout(&m);
layout.dbu (0.001);
db::Cell &top = layout.cell (layout.add_cell ("TOP"));
EXPECT_EQ (lib->layout ().cell_by_name ("TOP").first, true);
db::cell_index_type lib_top = lib->layout ().cell_by_name ("TOP").second;
db::cell_index_type lp1 = layout.get_lib_proxy (lib, lib_top);
EXPECT_EQ (std::string (layout.cell_name (lp1)), "TOP$1");
EXPECT_EQ (layout.basic_name (lp1), "TOP");
EXPECT_EQ (layout.display_name (lp1), "L.TOP");
EXPECT_EQ (layout.get_properties(0).to_string (), "23/0");
EXPECT_EQ (layout.get_properties(1).to_string (), "16/0");
EXPECT_EQ (layout.get_properties(2).to_string (), "24/0");
db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (lp1), db::Trans (db::Vector (0, 0))));
std::vector<tl::Variant> parameters;
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
tl::Variant &width = parameters[0];
tl::Variant &height = parameters[1];
tl::Variant &orientation = parameters[2];
width = 2.0;
height = 10.0;
orientation = (long)3;
EXPECT_EQ (lib->layout ().pcell_by_name ("PD").first, true);
db::pcell_id_type pd = lib->layout ().pcell_by_name ("PD").second;
db::cell_index_type lib_pd1 = lib->layout ().get_pcell_variant (pd, parameters);
db::cell_index_type lp2 = layout.get_lib_proxy (lib, lib_pd1);
EXPECT_EQ (std::string (layout.cell_name (lp2)), "PD$2");
EXPECT_EQ (layout.basic_name (lp2), "PD");
EXPECT_EQ (layout.display_name (lp2), "L.PD*");
const db::Cell *lp2_cell = &layout.cell (lp2);
EXPECT_EQ (dynamic_cast<const db::LibraryProxy *> (lp2_cell) != 0, true);
EXPECT_EQ (lp2_cell->is_proxy (), true);
EXPECT_EQ (layout.is_pcell_instance (lp2).first, true);
EXPECT_EQ (layout.is_pcell_instance (lp2).second, pd);
EXPECT_EQ (layout.get_pcell_parameters (lp2)[0].to_string(), std::string ("2"));
EXPECT_EQ (layout.get_pcell_parameters (lp2)[1].to_string(), std::string ("10"));
db::Instance i2 = top.insert (db::CellInstArray (db::CellInst (lp2), db::Trans (db::Vector (10000, 0))));
db::Writer writer = db::Writer (db::SaveLayoutOptions ());
/* produce golden:
tl::OutputStream stream ("lib_test.gds");
writer.write (layout, stream);
*/
equal = compare_vs_au (this, layout, "lib_test.gds");
EXPECT_EQ (equal, true);
// if not in editable mode, we could have lost the reference to the second instance
if (db::default_editable_mode ()) {
m.transaction ("x");
height = 5.0;
db::cell_index_type i2_cid = i2.cell_index ();
i2 = top.change_pcell_parameters (i2, parameters);
EXPECT_NE (i2.cell_index (), i2_cid);
EXPECT_EQ (std::string (layout.cell_name (i2.cell_index ())), "PD$3");
EXPECT_EQ (layout.basic_name (i2.cell_index ()), "PD");
EXPECT_EQ (layout.display_name (i2.cell_index ()), "L.PD*");
/* produce golden:
tl::OutputStream stream2 ("lib_test2.gds");
writer.write (layout, stream2);
*/
equal = compare_vs_au (this, layout, "lib_test2.gds");
EXPECT_EQ (equal, true);
m.commit ();
m.transaction ("y");
width = 0.5;
height = 1.0;
orientation = long (0);
i2 = top.change_pcell_parameters (i2, parameters);
/* produce golden:
tl::OutputStream stream3 ("lib_test3.gds");
writer.write (layout, stream3);
*/
EXPECT_EQ (std::string (layout.cell_name (i2.cell_index ())), "PD");
EXPECT_EQ (layout.basic_name (i2.cell_index ()), "PD");
EXPECT_EQ (layout.display_name (i2.cell_index ()), "L.PD*");
equal = compare_vs_au (this, layout, "lib_test3.gds");
EXPECT_EQ (equal, true);
m.commit ();
m.undo ();
equal = compare_vs_au (this, layout, "lib_test2.gds");
EXPECT_EQ (equal, true);
m.undo ();
equal = compare_vs_au (this, layout, "lib_test.gds");
EXPECT_EQ (equal, true);
m.redo ();
equal = compare_vs_au (this, layout, "lib_test2.gds");
EXPECT_EQ (equal, true);
}
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (l);
} catch (...) {
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (l);
throw;
}
std::vector<std::string> libnames_after;
for (db::LibraryManager::iterator il = db::LibraryManager::instance ().begin (); il != db::LibraryManager::instance ().end (); ++il) {
libnames_after.push_back (il->first);
}
std::sort (libnames_after.begin (), libnames_after.end ());
EXPECT_EQ (tl::join (libnames_before, ","), tl::join (libnames_after, ","));
}
TEST(2)
{
LIBT_L *lib = new LIBT_L (_this);
db::LibraryManager::instance ().register_lib (lib);
try {
bool equal;
db::Writer writer = db::Writer (db::SaveLayoutOptions ());
db::Manager m;
db::Layout layout(&m);
layout.dbu (0.001);
db::Cell &top = layout.cell (layout.add_cell ("TOP"));
db::cell_index_type lib_top = lib->layout ().cell_by_name ("TOP").second;
db::cell_index_type lp1 = layout.get_lib_proxy (lib, lib_top);
db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (lp1), db::Trans (db::Vector (0, 0))));
std::vector<tl::Variant> parameters;
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
parameters.push_back (tl::Variant ());
tl::Variant &width = parameters[0];
tl::Variant &height = parameters[1];
tl::Variant &orientation = parameters[2];
width = 2.0;
height = 10.0;
orientation = (long)3;
db::pcell_id_type pd = lib->layout ().pcell_by_name ("PD").second;
db::cell_index_type lib_pd1 = lib->layout ().get_pcell_variant (pd, parameters);
db::cell_index_type lp2 = layout.get_lib_proxy (lib, lib_pd1);
db::Instance i2 = top.insert (db::CellInstArray (db::CellInst (lp2), db::Trans (db::Vector (10000, 0))));
EXPECT_EQ (std::string (layout.cell_name (lp2)), "PD$2");
EXPECT_EQ (layout.basic_name (lp2), "PD");
EXPECT_EQ (layout.display_name (lp2), "L.PD*");
std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbLibraries2.gds"));
{
tl::OutputStream stream (tmp_file);
writer.write (layout, stream);
}
db::Layout tmp;
{
tl::InputStream tmp_stream (tmp_file);
db::Reader reader_tmp (tmp_stream);
reader_tmp.read (tmp);
}
std::pair<bool, db::cell_index_type> tmp_pd2 = tmp.cell_by_name ("PD$2");
EXPECT_EQ (tmp_pd2.first, true);
EXPECT_EQ (tmp.basic_name (tmp_pd2.second), "PD");
EXPECT_EQ (tmp.display_name (tmp_pd2.second), "L.PD*");
db::Instance tmp_i2 = tmp.cell (tmp_pd2.second).begin_parent_insts ()->child_inst ();
EXPECT_EQ (tmp_i2.cell_index (), tmp_pd2.second);
std::vector<tl::Variant> new_param = tmp.get_pcell_parameters (tmp_pd2.second);
EXPECT_EQ (new_param.size (), size_t (3));
EXPECT_EQ (new_param[0].to_string (), std::string ("2"));
EXPECT_EQ (new_param[1].to_string (), std::string ("10"));
EXPECT_EQ (new_param[2].to_string (), std::string ("3"));
std::pair<bool, db::cell_index_type> tt = tmp.cell_by_name ("TOP");
EXPECT_EQ (tt.first, true);
db::Cell &tmp_top = tmp.cell (tt.second);
if (db::default_editable_mode ()) {
new_param[1] = 5.0;
db::cell_index_type tmp_i2_cid = tmp_i2.cell_index ();
tmp_i2 = tmp_top.change_pcell_parameters (tmp_i2, new_param);
EXPECT_NE (tmp_i2.cell_index (), tmp_i2_cid);
EXPECT_EQ (std::string (tmp.cell_name (tmp_i2.cell_index ())), "PD$3");
EXPECT_EQ (tmp.basic_name (tmp_i2.cell_index ()), "PD");
EXPECT_EQ (tmp.display_name (tmp_i2.cell_index ()), "L.PD*");
/* produce golden:
tl::OutputStream stream3 ("lib_test2.gds");
writer.write (tmp, stream3);
*/
equal = compare_vs_au (this, tmp, "lib_test2.gds");
EXPECT_EQ (equal, true);
}
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (lib);
} catch (...) {
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (lib);
throw;
}
}
TEST(3)
{
LIBT_A *lib_a = new LIBT_A ();
db::LibraryManager::instance ().register_lib (lib_a);
LIBT_B *lib_b = new LIBT_B ();
db::LibraryManager::instance ().register_lib (lib_b);
try {
// This test tests the ability to reference libraries out of other libraries ("B" references "A"),
// the ability to persist that and whether this survives a write/read cycle.
db::Manager m;
db::Layout layout(&m);
layout.dbu (0.001);
db::Cell &top = layout.cell (layout.add_cell ("TOP"));
db::cell_index_type lib_bb = lib_b->layout ().cell_by_name ("B").second;
db::cell_index_type lp = layout.get_lib_proxy (lib_b, lib_bb);
db::Instance i1 = top.insert (db::CellInstArray (db::CellInst (lp), db::Trans (db::Vector (0, 0))));
std::string tmp_file = tl::TestBase::tmp_file (tl::sprintf ("tmp_dbLibraries3.gds"));
{
db::Writer writer = db::Writer (db::SaveLayoutOptions ());
tl::OutputStream stream (tmp_file);
writer.write (layout, stream);
}
layout.clear ();
db::Layout tmp;
{
tl::InputStream tmp_stream (tmp_file);
db::Reader reader_tmp (tmp_stream);
reader_tmp.read (tmp);
}
bool equal = compare_vs_au (this, tmp, "lib_test4.gds");
EXPECT_EQ (equal, true);
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (lib_a);
db::LibraryManager::instance ().delete_lib (lib_b);
} catch (...) {
// because we switch to editable mode in between we have to clear the repository explicitly. Otherwise it's being cleared
// on next entry of TEST which will cause a segmentation fault if editable mode is different then.
db::LibraryManager::instance ().delete_lib (lib_a);
db::LibraryManager::instance ().delete_lib (lib_b);
throw;
}
}