Merge pull request #1850 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2024-09-21 20:33:21 +02:00 committed by GitHub
commit ff0a2b8ab7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 816 additions and 161 deletions

191
scripts/compare_ut_performance.rb Executable file
View File

@ -0,0 +1,191 @@
#!/bin/ruby
require "nokogiri"
# Collect files from command line
files = []
time_class = :wall
sort_key = :name
ARGV.each do |arg|
if arg =~ /^--help|-h/
puts <<"END"
#{$0} [options] <file1> <file2> ...
The files are XML files produced by "ut_runner" with the -a option.
Options are:
-w Use wall time (default)
-u Use user time
-s Sort by average time, lowest first
+s Sort by average time, largest first
The script reads these files are compares performance (user and wall times)
of the different tests.
END
exit(0)
elsif arg == "-w"
time_class = :wall
elsif arg == "-u"
time_class = :user
elsif arg == "-s"
sort_key = :time_up
elsif arg == "+s"
sort_key = :time_down
elsif arg =~ /^-/
puts("*** ERROR: unknown option #{arg}. Use -h for help.")
exit(1)
else
files << arg
end
end
# A class representing the data from one test
class TestData
def initialize(file)
@file = file
@data = {}
File.open(file) do |f|
doc = Nokogiri::XML(f)
doc.xpath("//testsuite").each do |testsuite|
ts_name = testsuite.at_xpath("@name").content
testsuite.xpath("testcase").each do |testcase|
tc_name = testcase.at_xpath("@name").content
times = testcase.at_xpath("x-testcase-times")
if times
wall_time = times.at_xpath("@wall").content.to_f
user_time = times.at_xpath("@user").content.to_f
@data[ [ts_name, tc_name] ] = [ wall_time, user_time ]
end
end
end
end
end
def file
@file
end
def keys
@data.keys
end
def times(key)
@data[key]
end
end
# Read the tests
tests = []
files.each do |f|
puts("Reading test file #{f} ..")
tests << TestData::new(f)
end
puts "Reading done."
puts ""
# Build the comparison table
all_tests = {}
tests.each_with_index do |test,index|
test.keys.each do |k|
all_tests[k] ||= [nil] * tests.size
all_tests[k][index] = test.times(k)
end
end
# print the result
tests.each_with_index do |test,index|
puts "(#{index + 1}) #{test.file}"
end
puts ""
time_index = 0
if time_class == :wall
puts "Wall times"
elsif time_class == :user
time_index = 1
puts "User times"
end
puts ""
l1 = all_tests.keys.collect { |k| k[0].size }.max
l2 = all_tests.keys.collect { |k| k[1].size }.max
fmt = "%-#{l1}s %-#{l2}s " + (["%15s"] * tests.size).join(" ") + " %15s %15s %10s"
title = fmt % ([ "Testsuite", "Test", ] + tests.each_with_index.collect { |t,i| "(#{i + 1})" } + [ "Min", "Max", "Delta" ])
puts title
puts "-" * title.size
total = [0.0] * tests.size
lines = []
all_tests.keys.sort { |a,b| a <=> b }.each do |k|
times = all_tests[k].collect { |t| t && t[time_index] }
min = max = delta = nil
if ! times.index(nil)
times.each_with_index do |t,i|
total[i] += t
end
min = times.min
max = times.max
if times.size > 1 && (max + min).abs > 1.0
delta = (max - min) / 0.5 / (max + min)
end
end
line = fmt % (k + times.collect { |t| t ? ("%.6f" % t) : "" } + [ min ? "%.6f" % min : "", max ? "%.6f" % max : "", delta ? "%.2f%%" % (delta * 100) : ""])
if sort_key == :time_up
lines << [ min && max ? min + max : 0.0, line ]
elsif sort_key == :time_down
lines << [ min && max ? -(min + max) : 0.0, line ]
else
lines << [ k, line ]
end
end
lines.sort { |a,b| a[0] <=> b[0] }.each do |k,line|
puts line
end
# Add total row
min = total.min
max = total.max
delta = nil
if total.size > 1 && (max + min).abs > 1.0
delta = (max - min) / 0.5 / (max + min)
end
puts ""
puts fmt % ([ "Total" , "" ] + total.collect { |t| t ? ("%.6f" % t) : "" } + [ min ? "%.6f" % min : "", max ? "%.6f" % max : "", delta ? "%.2f%%" % (delta * 100) : ""])

View File

@ -2876,6 +2876,9 @@ Layout::get_context_info (cell_index_type cell_index, LayoutOrCellContextInfo &i
// one level of library indirection
ly = &lib->layout ();
if (! ly->is_valid_cell_index (lib_proxy->library_cell_index ())) {
return any_meta; // abort
}
cptr = &ly->cell (lib_proxy->library_cell_index ());
info.lib_name = lib->get_name ();

View File

@ -132,6 +132,7 @@ LibraryProxy::get_layer_indices (db::Layout &layout, db::ImportLayerMapping *lay
Library *lib = LibraryManager::instance ().lib (lib_id ());
tl_assert (lib != 0);
tl_assert (lib->layout ().is_valid_cell_index (library_cell_index ()));
const db::Cell &cell = lib->layout ().cell (library_cell_index ());
@ -247,11 +248,11 @@ LibraryProxy::get_basic_name () const
{
Library *lib = LibraryManager::instance ().lib (lib_id ());
if (lib) {
const db::Cell *lib_cell = &lib->layout ().cell (library_cell_index ());
if (! lib_cell) {
if (! lib->layout ().is_valid_cell_index (library_cell_index ())) {
return "<defunct>";
} else {
return lib_cell->get_basic_name ();
const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ());
return lib_cell.get_basic_name ();
}
} else {
return Cell::get_basic_name ();
@ -263,11 +264,11 @@ LibraryProxy::get_display_name () const
{
Library *lib = LibraryManager::instance ().lib (lib_id ());
if (lib) {
const db::Cell *lib_cell = &lib->layout ().cell (library_cell_index ());
if (! lib_cell) {
if (! lib->layout ().is_valid_cell_index (library_cell_index ())) {
return lib->get_name () + "." + "<defunct>";
} else {
return lib->get_name () + "." + lib_cell->get_display_name ();
const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ());
return lib->get_name () + "." + lib_cell.get_display_name ();
}
} else {
return Cell::get_display_name ();
@ -279,11 +280,11 @@ LibraryProxy::get_qualified_name () const
{
Library *lib = LibraryManager::instance ().lib (lib_id ());
if (lib) {
const db::Cell *lib_cell = &lib->layout ().cell (library_cell_index ());
if (! lib_cell) {
if (! lib->layout ().is_valid_cell_index (library_cell_index ())) {
return lib->get_name () + "." + "<defunct>";
} else {
return lib->get_name () + "." + lib_cell->get_qualified_name ();
const db::Cell &lib_cell = lib->layout ().cell (library_cell_index ());
return lib->get_name () + "." + lib_cell.get_qualified_name ();
}
} else {
return Cell::get_qualified_name ();

View File

@ -61,7 +61,7 @@ join_layer_names (std::string &s, const std::string &n)
// ReaderBase implementation
ReaderBase::ReaderBase ()
: m_warnings_as_errors (false), m_warn_level (1)
: m_warnings_as_errors (false), m_warn_level (1), m_warn_count_for_same_message (0), m_first_warning (true)
{
}
@ -79,6 +79,39 @@ void
ReaderBase::init (const db::LoadLayoutOptions &options)
{
m_warn_level = options.warn_level ();
m_last_warning.clear ();
m_warn_count_for_same_message = 0;
m_first_warning = true;
}
bool
ReaderBase::first_warning ()
{
bool f = m_first_warning;
m_first_warning = false;
return f;
}
int
ReaderBase::compress_warning (const std::string &msg)
{
const int max_warnings = 10;
if (! msg.empty () && msg == m_last_warning) {
if (m_warn_count_for_same_message < max_warnings) {
++m_warn_count_for_same_message;
return -1;
} else if (m_warn_count_for_same_message == max_warnings) {
++m_warn_count_for_same_message;
return 0;
} else {
return 1;
}
} else {
m_last_warning = msg;
m_warn_count_for_same_message = 0;
return -1;
}
}
// ---------------------------------------------------------------

View File

@ -126,12 +126,27 @@ public:
return m_warn_level;
}
/**
* @brief Returns true (once) if this is the first warning
*/
bool first_warning ();
/**
* @brief Returns a value indicating whether to compress the given warning
*
* The return value is either -1 (do not skip), 0 (first warning not to be shown), 1 (warning not shown(.
*/
int compress_warning (const std::string &msg);
protected:
virtual void init (const db::LoadLayoutOptions &options);
private:
bool m_warnings_as_errors;
int m_warn_level;
std::string m_last_warning;
int m_warn_count_for_same_message;
bool m_first_warning;
};
/**

View File

@ -367,9 +367,8 @@ static tl::Variant get_layout_property (const db::Layout *l, const tl::Variant &
}
}
static tl::Variant get_layout_properties (const db::Layout *layout)
static tl::Variant get_properties_hash (const db::Layout *layout, db::properties_id_type id)
{
db::properties_id_type id = layout->prop_id ();
if (id == 0) {
return tl::Variant::empty_array ();
}
@ -382,6 +381,11 @@ static tl::Variant get_layout_properties (const db::Layout *layout)
return res;
}
static tl::Variant get_layout_properties (const db::Layout *layout)
{
return get_properties_hash (layout, layout->prop_id ());
}
static db::cell_index_type cell_by_name (db::Layout *l, const char *name)
{
std::pair<bool, db::cell_index_type> c = l->cell_by_name (name);
@ -619,6 +623,18 @@ static db::properties_id_type properties_id (db::Layout *layout, const std::vect
return layout->properties_repository ().properties_id (props);
}
static db::properties_id_type properties_id_from_hash (db::Layout *layout, const std::map<tl::Variant, tl::Variant> &properties)
{
db::PropertiesRepository::properties_set props;
for (std::map<tl::Variant, tl::Variant>::const_iterator v = properties.begin (); v != properties.end (); ++v) {
db::property_names_id_type name_id = layout->properties_repository ().prop_name_id (v->first);
props.insert (std::make_pair (name_id, v->second));
}
return layout->properties_repository ().properties_id (props);
}
static std::vector<tl::Variant> properties (const db::Layout *layout, db::properties_id_type id)
{
std::vector<tl::Variant> ret;
@ -1324,7 +1340,7 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("property", &get_layout_property, gsi::arg ("key"),
"@brief Gets the user property with the given key\n"
"@brief Gets the Layout's user property with the given key\n"
"This method is a convenience method that gets the property with the given key. "
"If no property with that key exists, it will return nil. Using that method is more "
"convenient than using the properties ID to retrieve the property value. "
@ -1332,8 +1348,8 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("properties", &get_layout_properties,
"@brief Gets the user properties as a hash\n"
"This method is a convenience method that gets all user properties as a single hash.\n"
"@brief Gets the Layout's user properties as a hash\n"
"This method is a convenience method that gets all user properties of the Layout object as a single hash.\n"
"\n"
"This method has been introduced in version 0.29.5."
) +
@ -1348,16 +1364,42 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"@param properties The array of pairs of variants (both elements can be integer, double or string)\n"
"@return The unique properties ID for that set"
) +
gsi::method_ext ("properties", &properties, gsi::arg ("properties_id"),
gsi::method_ext ("properties_id", &properties_id_from_hash, gsi::arg ("properties"),
"@brief Gets the properties ID for a given properties set\n"
"\n"
"This variant accepts a hash of value vs. key for the properties instead of array of key/value pairs. "
"Apart from this, it behaves like the other \\properties_id variant.\n"
"\n"
"@param properties A hash of property keys/values (both keys and values can be integer, double or string)\n"
"@return The unique properties ID for that set\n"
"\n"
"This variant has been introduced in version 0.29.7."
) +
gsi::method_ext ("properties_array|#properties", &properties, gsi::arg ("properties_id"),
"@brief Gets the properties set for a given properties ID\n"
"\n"
"Basically performs the backward conversion of the 'properties_id' method. "
"Given a properties ID, returns the properties set as an array of pairs of "
"variants. In this array, each key and the value are stored as pairs (arrays with two elements).\n"
"Basically this method performs the backward conversion of the 'properties_id' method. "
"Given a properties ID, it returns the properties set as an array. "
"In this array, each key and the value is stored as a pair (an array with two elements).\n"
"If the properties ID is not valid, an empty array is returned.\n"
"A version that returns a hash instead of pairs of key/values, is \\properties_hash.\n"
"\n"
"@param properties_id The properties ID to get the properties for\n"
"@return The array of variants (see \\properties_id)\n"
"@return An array of key/value pairs (see \\properties_id)\n"
"\n"
"The 'properties_array' alias was introduced in version 0.29.7 and the plain 'properties' alias was deprecated."
) +
gsi::method_ext ("properties_hash", &get_properties_hash, gsi::arg ("properties_id"),
"@brief Gets the properties set for a given properties ID as a hash\n"
"\n"
"Returns the properties for a given properties ID as a hash.\n"
"It is a convenient alternative to \\properties_array, which returns "
"an array of key/value pairs.\n"
"\n"
"@param properties_id The properties ID to get the properties for\n"
"@return The hash representing the properties for the given ID (values vs. key)\n"
"\n"
"This method has been introduced in version 0.29.7."
) +
gsi::method ("unique_cell_name", &db::Layout::uniquify_cell_name, gsi::arg ("name"),
"@brief Creates a new unique cell name from the given name\n"

View File

@ -44,11 +44,6 @@ static db::LayoutToNetlist *make_l2n_from_existing_dss_with_layout (db::DeepShap
return new db::LayoutToNetlist (dss, layout_index);
}
static db::LayoutToNetlist *make_l2n_from_existing_dss (db::DeepShapeStore *dss)
{
return new db::LayoutToNetlist (dss);
}
static db::LayoutToNetlist *make_l2n_flat (const std::string &topcell_name, double dbu)
{
return new db::LayoutToNetlist (topcell_name, dbu);
@ -225,12 +220,23 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@brief Creates a new extractor connected to an original layout\n"
"This constructor will attach the extractor to an original layout through the "
"shape iterator.\n"
"\n"
"The shape iterator does not need to be an actual shape iterator. It is merely used to identify the original "
"layout and provides additional parameters such as the top cell to use and advanced options such as subtree pruning.\n"
"\n"
"You can construct a dummy iteraor usable for this purpose without layers using an empty layer set:\n"
"\n"
"@code\n"
"ly = ... # external layout\n"
"l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))"
"..."
"@/code\n"
) +
gsi::constructor ("new", &make_l2n_default,
"@brief Creates a new and empty extractor object\n"
"The main objective for this constructor is to create an object suitable for reading an annotated netlist.\n"
) +
gsi::constructor ("new", &make_l2n_from_existing_dss, gsi::arg ("dss"),
gsi::constructor ("new", &make_l2n_from_existing_dss_with_layout, gsi::arg ("dss"), gsi::arg ("layout_index", 0),
"@brief Creates a new extractor object reusing an existing \\DeepShapeStore object\n"
"This constructor can be used if there is a DSS object already from which the "
"shapes can be taken. This version can only be used with \\register to "
@ -241,14 +247,8 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"The extractor will not take ownership of the dss object unless you call \\keep_dss."
) +
gsi::constructor ("new", &make_l2n_from_existing_dss_with_layout, gsi::arg ("dss"), gsi::arg ("layout_index"),
"@brief Creates a new extractor object reusing an existing \\DeepShapeStore object\n"
"This constructor can be used if there is a DSS object already from which the "
"shapes can be taken. NOTE: in this case, the make_... functions will create "
"new layers inside this DSS. To register existing layers (regions) use \\register.\n"
) +
gsi::constructor ("new", &make_l2n_flat, gsi::arg ("topcell_name"), gsi::arg ("dbu"),
"@brief Creates a new extractor object with a flat DSS\n"
"@brief Creates a new, detached extractor object with a flat DSS\n"
"@param topcell_name The name of the top cell of the internal flat layout\n"
"@param dbu The database unit to use for the internal flat layout\n"
"\n"
@ -258,6 +258,25 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"The database unit is mandatory because the physical parameter extraction "
"for devices requires this unit for translation of layout to physical dimensions.\n"
"\n"
"Example:\n"
"\n"
"@code\n"
"rmetal1 = ... # an external Region object representing 'metal1' layer\n"
"rvia11 = ... # an external Region object representing 'via1' layer\n"
"rmetal2 = ... # an external Region object representing 'metal2' layer\n"
"\n"
"l2n = RBA::LayoutToNetlist::new(\"TOP_CELL\", 0.001)\n"
"# imports the external Regions as flat ones and assigns proper names\n"
"l2n.register(rmetal1, \"metal1\")\n"
"l2n.register(rvia1, \"via1\")\n"
"l2n.register(rmetal2, \"metal2\")\n"
"\n"
"# intra- and inter-layer connects:\n"
"l2n.connect(metal1)\n"
"l2n.connect(metal1, via1)\n"
"l2n.connect(metal2)\n"
"@/code\n"
) +
gsi::method ("generator", &db::LayoutToNetlist::generator,
"@brief Gets the generator string.\n"
@ -268,9 +287,11 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
) +
gsi::method ("dss", (db::DeepShapeStore &(db::LayoutToNetlist::*) ()) &db::LayoutToNetlist::dss,
"@brief Gets a reference to the internal DSS object.\n"
"See the class description for details about the DSS object used inside the LayoutToNetlist object."
) +
gsi::method ("keep_dss", &db::LayoutToNetlist::keep_dss,
"@brief Resumes ownership over the DSS object if created with an external one.\n"
"@brief Takes ownership of the DSS object if the LayoutToNetlist object was created with an external one.\n"
"See the class description for details about the DSS object used inside the LayoutToNetlist object."
) +
gsi::method ("threads=", &db::LayoutToNetlist::set_threads, gsi::arg ("n"),
"@brief Sets the number of threads to use for operations which support multiple threads\n"
@ -329,13 +350,28 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
) +
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (const db::ShapeCollection &region) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer\n"
"The layer is given by a \\ShapeCollection object which is either a \\Region or \\Texts object.\n"
"This \\Region or \\Texts object needs to be either one that was created by make_... or one that\n"
"was registered through \\register. This function will return the name that was given to that \\Region or \\Texts "
"during those operations."
"\n"
"You can use \\layer_by_name to retrieve the \\Region object of a layer from the layer index.\n"
) +
gsi::method ("layer_index", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &region) const) &db::LayoutToNetlist::layer_of<db::ShapeCollection>, gsi::arg ("l"),
"@brief Gets the layer index for the given data object\n"
"This method is essentially identical to \\layer_of, but uses \\ShapeCollection, which is a polymorphic base class "
"for a variety of shape containers.\n"
"\n"
"The layer index returned is index of the corresponding layer inside the internal layout (see \\internal_layout).\n"
"The layer index is more handy to use for identifying a layer than a \\Region of \\Texts object, for example when using it as a key in hashes.\n"
"\n"
"You can use \\layer_by_index to retrieve the \\Region object of a layer from the layer index.\n"
"\n"
"This method has been introduced in version 0.29.3.\n"
) +
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer (by index)\n"
"See \\layer_index for a description of the layer index.\n"
) +
gsi::method ("register", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()),
"@brief Names the given layer\n"
@ -345,11 +381,16 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"Registering will copy the shapes into the LayoutToNetlist object in this step to enable "
"netlist extraction.\n"
"\n"
"Naming a layer allows the system to indicate the layer in various contexts, i.e. "
"when writing the data to a file. Named layers are also persisted inside the LayoutToNetlist object. "
"They are not discarded when the Region object is destroyed.\n"
"External \\Region or \\Texts objects that are registered are persisted. This means "
"the LayoutToNetlist object becomes owner of them and they are not discarded when the "
"Region or Text object is destroyed.\n"
"\n"
"If required, the system will assign a name automatically."
"Naming a layer allows allows retrieving the layer later, for example after the LayoutToNetlist object\n"
"has been written to a file and restored from that file (during this process, the layer indexes will change).\n"
"\n"
"If no name is given, the system will assign a name automatically.\n"
"It is recommended to specify a name if it is required to identify the layer later - for example for "
"retrieving shapes from it.\n"
"\n"
"This method has been generalized in version 0.27. Starting with version 0.29.3, the index of the layer is returned.\n"
) +
@ -360,6 +401,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@brief Returns a list of indexes of the layers kept inside the LayoutToNetlist object.\n"
"You can use \\layer_name to get the name from a layer index. You can use \\layer_info to get "
"the \\LayerInfo object attached to a layer - if the layer is an original layer.\n"
"You can use \\layer_by_index to get the \\Region object for the layer by index.\n"
"\n"
"This method has been introduced in version 0.29.2.\n"
) +
@ -369,70 +411,109 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"stream layer information where the original layer was taken from. Otherwise an empty \\LayerInfo object "
"is returned.\n"
"\n"
"The LayerInfo object is usually empty for derived layers - i.e. those which are computed through "
"boolean operations for example. It is recommended to assign names to such layers for easy identification later.\n"
"\n"
"This method has been introduced in version 0.29.2.\n"
) +
gsi::factory ("layer_by_name", &db::LayoutToNetlist::layer_by_name, gsi::arg ("name"),
"@brief Gets a layer object for the given name.\n"
"The returned object is a copy which represents the named layer."
"The returned object is a new Region object representing the named layer. It will refer to a layer inside the "
"internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n"
"The method returns 'nil' if the name is not a valid layer name.\n"
"See \\register and the make_... methods for a description of layer naming.\n"
) +
gsi::factory ("layer_by_index", &db::LayoutToNetlist::layer_by_index, gsi::arg ("index"),
"@brief Gets a layer object for the given index.\n"
"Only named layers can be retrieved with this method. "
"The returned object is a copy which represents the named layer."
"The returned object is a new Region object representing the layer with the given index. It will refer to a layer inside the "
"internal layout, or more specifically inside the \\DeepShapeStorage object (see \\dss and \\internal_layout).\n"
"The method returns 'nil' if the index is not a valid layer index."
) +
gsi::method ("is_persisted?", &db::LayoutToNetlist::is_persisted<db::Region>, gsi::arg ("layer"),
"@brief Returns true, if the given layer is a persisted region.\n"
"Persisted layers are kept inside the LayoutToNetlist object and are not released "
"if their object is destroyed. Named layers are persisted, unnamed layers are not. "
"Only persisted, named layers can be put into \\connect."
"if their object is destroyed. Layers created with make_... or registered through \\register are persisted.\n"
"This basically applies to all layers, except intermediate layers that are potentially created as results of "
"operations between layers and which are not registered.\n"
) +
gsi::method ("is_persisted?", &db::LayoutToNetlist::is_persisted<db::Texts>, gsi::arg ("layer"),
"@brief Returns true, if the given layer is a persisted texts collection.\n"
"Persisted layers are kept inside the LayoutToNetlist object and are not released "
"if their object is destroyed. Named layers are persisted, unnamed layers are not. "
"Only persisted, named layers can be put into \\connect.\n"
"if their object is destroyed. Layers created with make_... or registered through \\register are persisted.\n"
"This basically applies to all layers, except intermediate layers that are potentially created as results of "
"operations between layers and which are not registered.\n"
"\n"
"The variant for Texts collections has been added in version 0.27."
) +
gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("name", std::string ()),
"@brief Creates a new, empty hierarchical region\n"
"\n"
"This method will create a new, empty layer inside the internal layout and register it.\n"
"It returns a new Region object that represents the new layer. See the class description for more details.\n"
"The name is optional. If given, the layer will already be named accordingly (see \\register).\n"
) +
gsi::factory ("make_layer", (db::Region *(db::LayoutToNetlist::*) (unsigned int, const std::string &)) &db::LayoutToNetlist::make_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()),
"@brief Creates a new hierarchical region representing an original layer\n"
"'layer_index' is the layer index of the desired layer in the original layout.\n"
"This variant produces polygons and takes texts for net name annotation.\n"
"This variant produces polygons and takes texts for net name annotation as special, property-annotated polygons.\n"
"A variant not taking texts is \\make_polygon_layer. A Variant only taking\n"
"texts is \\make_text_layer.\n"
"\n"
"The name is optional. If given, the layer will already be named accordingly (see \\register).\n"
"This method will basically create a copy of the original layer inside the internal layout - more specifically inside "
"the DSS (see \\dss and \\internal_layout). It returns a new Region object that represents this layer copy. "
"The new layer is already registered with the given name and can be used for \\connect for example.\n"
) +
gsi::factory ("make_text_layer", &db::LayoutToNetlist::make_text_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()),
"@brief Creates a new region representing an original layer taking texts only\n"
"See \\make_layer for details.\n"
"\n"
"The name is optional. If given, the layer will already be named accordingly (see \\register).\n"
"\n"
"Starting with version 0.27, this method returns a \\Texts object."
) +
gsi::factory ("make_polygon_layer", &db::LayoutToNetlist::make_polygon_layer, gsi::arg ("layer_index"), gsi::arg ("name", std::string ()),
"@brief Creates a new region representing an original layer taking polygons and texts\n"
"@brief Creates a new region representing an original layer taking polygons only\n"
"See \\make_layer for details.\n"
"\n"
"The name is optional. If given, the layer will already be named accordingly (see \\register).\n"
) +
gsi::method ("extract_devices", &db::LayoutToNetlist::extract_devices, gsi::arg ("extractor"), gsi::arg ("layers"),
"@brief Extracts devices\n"
"See the class description for more details.\n"
"This method will run device extraction for the given extractor. The layer map is specific\n"
"for the extractor and uses the region objects derived with \\make_layer and its variants.\n"
"for the extractor and uses the Region objects derived with \\make_layer and its variants or Region "
"objects registered through \\register. The layer map keys are the inputs layers defined for the\n"
"specific extractor, but also the output layers where the extractor places markers for the device terminals.\n"
"\n"
"In addition, derived regions can be passed too. Certain limitations apply. It's safe to use\n"
"In addition, derived regions can also be passed to the device extractor inside the layer map.\n"
"Certain limitations apply. It is usually safe to use\n"
"boolean operations for deriving layers. Other operations are applicable as long as they are\n"
"capable of delivering hierarchical layers.\n"
"\n"
"If errors occur, the device extractor will contain theses errors.\n"
"Example:\n"
"\n"
"@code\n"
"ly = ... # original Layout\n"
"\n"
"l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, []))\n"
"rnwell = l2n.make_layer(ly.layer(1, 0), \"nwell\" )\n"
"ractive = l2n.make_layer(ly.layer(2, 0), \"active\" )\n"
"rpoly = l2n.make_layer(ly.layer(3, 0), \"poly\" )\n"
"\n"
"rpactive = ractive & rnwell\n"
"rpgate = rpactive & rpoly\n"
"rpsd = rpactive - rpgate\n"
"\n"
"rnactive = ractive - rnwell\n"
"rngate = rnactive & rpoly\n"
"rnsd = rnactive - rngate\n"
"\n"
"# PMOS transistor device extraction\n"
"pmos_ex = RBA::DeviceExtractorMOS3Transistor::new(\"PMOS\")\n"
"l2n.extract_devices(pmos_ex, { \"SD\" => rpsd, \"G\" => rpgate, \"P\" => rpoly })\n"
"\n"
"# NMOS transistor device extraction\n"
"nmos_ex = RBA::DeviceExtractorMOS3Transistor::new(\"NMOS\")\n"
"l2n.extract_devices(nmos_ex, { \"SD\" => rnsd, \"G\" => rngate, \"P\" => rpoly })\n"
"@/code\n"
"\n"
"If errors occur, they will be logged inside the device extractor object and copied to the log of this LayoutToNetlist object (self).\n"
) +
gsi::method ("reset_extracted", &db::LayoutToNetlist::reset_extracted,
"@brief Resets the extracted netlist and enables re-extraction\n"
@ -448,10 +529,12 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
) +
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"),
"@brief Defines an intra-layer connection for the given layer.\n"
"The layer is either an original layer created with \\make_includelayer and its variants or\n"
"a derived layer. Certain limitations apply. It's safe to use\n"
"The layer as a Region object, representing either an original layer created with \\make_layer and its variants or\n"
"a derived layer which was registered using \\register. Certain limitations apply. It's safe to use\n"
"boolean operations for deriving layers. Other operations are applicable as long as they are\n"
"capable of delivering hierarchical layers.\n"
"capable of delivering hierarchical layers. Operations that introduce flat layers will create additonal pins\n"
"as connections need to be made from a subcell to the top cell. Hence, flat layers - or rather some with a bad hierarchy - should\n"
"be avoided in \\connect.\n"
) +
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &, const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("a"), gsi::arg ("b"),
"@brief Defines an inter-layer connection for the given layers.\n"
@ -544,12 +627,6 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This attribute has been introduced in version 0.27.\n"
) +
gsi::method ("make_soft_connection_diodes=", &db::LayoutToNetlist::set_make_soft_connection_diodes, gsi::arg ("flag"),
"@hide"
) +
gsi::method ("make_soft_connection_diodes", &db::LayoutToNetlist::make_soft_connection_diodes,
"@hide"
) +
gsi::method ("top_level_mode=", &db::LayoutToNetlist::set_top_level_mode, gsi::arg ("flag"),
"@brief Sets a flag indicating whether top level mode is enabled.\n"
"\n"
@ -640,12 +717,16 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"The internal layout is where the LayoutToNetlist database stores the shapes for the nets. "
"Usually you do not need to access this object - you must use \\build_net or \\shapes_of_net to "
"retrieve the per-net shape information. If you access the internal layout, make sure you do not "
"modify it."
"modify it.\n"
"\n"
"See the class description for details about the internal layout object."
) +
gsi::method_ext ("internal_top_cell", &l2n_internal_top_cell,
"@brief Gets the internal top cell\n"
"Usually it should not be required to obtain the internal cell. If you need to do so, make sure not to modify the cell as\n"
"the functionality of the netlist extractor depends on it."
"the functionality of the netlist extractor depends on it.\n"
"\n"
"See the class description for details about the internal layout object."
) +
gsi::method ("layer_of", &db::LayoutToNetlist::layer_of<db::Region>, gsi::arg ("l"),
"@brief Gets the internal layer for a given extraction layer\n"
@ -950,57 +1031,88 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"This variant has been introduced in version 0.26.6.\n"
) +
// test API
gsi::method ("make_soft_connection_diodes=", &db::LayoutToNetlist::set_make_soft_connection_diodes, gsi::arg ("flag"), "@hide") +
gsi::method ("make_soft_connection_diodes", &db::LayoutToNetlist::make_soft_connection_diodes, "@hide") +
gsi::method_ext ("dump_joined_net_names", &dump_joined_net_names, "@hide") +
gsi::method_ext ("dump_joined_net_names_per_cell", &dump_joined_net_names_per_cell, "@hide") +
gsi::method_ext ("dump_joined_nets", &dump_joined_nets, "@hide") +
gsi::method_ext ("dump_joined_nets_per_cell", &dump_joined_nets_per_cell, "@hide")
,
"@brief A generic framework for extracting netlists from layouts\n"
"@brief A framework for extracting netlists from layouts\n"
"\n"
"This class wraps various concepts from db::NetlistExtractor and db::NetlistDeviceExtractor\n"
"and more. It is supposed to provide a framework for extracting a netlist from a layout.\n"
"This class provides a framework for extracting a netlist from a layout.\n"
"\n"
"The use model of this class consists of five steps which need to be executed in this order.\n"
"A LayoutToNetlist object extracts a netlist from an external \\Layout. To do so, it keeps "
"an internal copy with an optimized representation of the original layout. When a netlist is extracted "
"the net geometries can be recovered from that internal layout. In addition to that layout, it keeps "
"the extracted netlist. Netlist and internal layout form a pair and there are references between them. "
"For example, the \\Circuit objects from the netlist have an attribute \\cell_index, which tells what cell "
"from the internal layout the circuit was derived from. In the same way, subcircuit references refer to "
"cell instances and nets keep a reference to the shapes they were derived from.\n"
"\n"
"LayoutToNetlist can also operate in detached mode, when there is no external layout. In this mode, "
"layers are created inside the internal layout only. As there is no input hierarchy, operation is "
"necessarily flat in that case. Single \\Region and \\Texts shape collections can be introduced into "
"the LayoutToNetlist objects from external sources to populate the layers from the internal layout.\n"
"For detached mode, use the 'LayoutToNetlist(topcell, dbu)' constructor.\n"
"\n"
"Usually, the internal layout is stored inside an internal \\DeepShapeStore object, which supplies "
"additional services such as layer lifetime management and maintains the connection to and from "
"the external layout.\n"
"However, you can also use the extractor with an existing \\DeepShapeStore object.\n"
"In that case, this external \\DeepShapeStore object is used instead of the internal one.\n"
"\n"
"The LayoutToNetlist object can be persisted into a 'Layout to netlist database' file. This database "
"is a storage for both the netlist and the net or circuit geometries. When reading such file into "
"a new LayoutToNetlist object, there will be no connection to any external layout, but all the "
"essential netlist and geometry information will be available.\n"
"\n"
"The LayoutToNetlist object is also the entry point for netlist-driven algorithms such as antenna checks.\n"
"\n"
"The use model of the LayoutToNetlist object consists of five steps which need to be executed in this order.\n"
"\n"
"@ul\n"
"@li Configuration: in this step, the LayoutToNetlist object is created and\n"
"@li @b Configuration: @/b\n"
" In this step, the LayoutToNetlist object is created and\n"
" if required, configured. Methods to be used in this step are \\threads=,\n"
" \\area_ratio= or \\max_vertex_count=. The constructor for the LayoutToNetlist\n"
" object receives a \\RecursiveShapeIterator object which basically supplies the\n"
" hierarchy and the layout taken as input.\n"
" hierarchy and the external layout taken as input. The constructor will initialize\n"
" the internal layout and connect it to the external one.\n"
"@/li\n"
"@li Preparation\n"
"@li @b Preparation: @/b\n"
" In this step, the device recognition and extraction layers are drawn from\n"
" the framework. Derived can now be computed using boolean operations.\n"
" Methods to use in this step are \\make_layer and its variants.\n"
" the framework. Derived layers can now be computed using boolean operations.\n"
" Methods to use in this step are \\make_layer and its variants. \\make_layer will either create\n"
" a new, empty layer or pull a layer from the external layout into the internal layout.\n"
" Derived layers are computed using the \\Region or \\Texts objects representing\n"
" existing layers. If derived layers are to be used in connectivity, they\n"
" need to be registered using \\register. This makes the LayoutToNetlist object the owner of "
" the layer (the layer is said to be persisted then). Registered layers can or should be given a "
" name. That helps indentifying them later.\n"
" Layer preparation is not necessarily required to happen before all\n"
" other steps. Layers can be computed shortly before they are required.\n"
"@/li\n"
"@li Following the preparation, the devices can be extracted using \\extract_devices.\n"
"@li @b Device extraction: @/b\n"
" Following the preparation, the devices can be extracted using \\extract_devices.\n"
" This method needs to be called for each device extractor required. Each time,\n"
" a device extractor needs to be given plus a map of device layers. The device\n"
" a device extractor needs to be given, plus a map of device layers. The device\n"
" layers are device extractor specific. Either original or derived layers\n"
" may be specified here. Layer preparation may happen between calls to \\extract_devices.\n"
"@/li\n"
"@li Once the devices are derived, the netlist connectivity can be defined and the\n"
"@li @b Connectivity definition: @/b\n"
" Once the devices are derived, the netlist connectivity can be defined and the\n"
" netlist extracted. The connectivity is defined with \\connect and its\n"
" flavours. The actual netlist extraction happens with \\extract_netlist.\n"
"@/li\n"
"@li After netlist extraction, the information is ready to be retrieved.\n"
"@li @b Netlist extraction: @/b\n"
" After netlist extraction, the information is ready to be retrieved.\n"
" The produced netlist is available with \\netlist. The Shapes of a\n"
" specific net are available with \\shapes_of_net. \\probe_net allows\n"
" finding a net by probing a specific location.\n"
"@/li\n"
"@/ul\n"
"\n"
"You can also use the extractor with an existing \\DeepShapeStore object "
"or even flat data. In this case, preparation means importing existing regions "
"with the \\register method.\n"
"If you want to use the \\LayoutToNetlist object with flat data, use the "
"'LayoutToNetlist(topcell, dbu)' constructor. If you want to use it with "
"hierarchical data and an existing DeepShapeStore object, use the "
"'LayoutToNetlist(dss)' constructor.\n"
"\n"
"Once the extraction is done, you can persist the \\LayoutToNetlist object "
"using \\write and restore it using \\read. You can use the query API (see below) to "
"analyze the LayoutToNetlist database.\n"
@ -1008,7 +1120,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"The query API of the \\LayoutToNetlist object consists of the following parts:\n"
"\n"
"@ul\n"
"@li Net shape retrieval: \\build_all_nets, \\build_nets, \\build_net and \\shapes_per_net @/li\n"
"@li Net shape retrieval: \\build_all_nets, \\build_nets, \\build_net and \\shapes_of_net @/li\n"
"@li Layers: \\layer_by_index, \\layer_by_name, \\layer_indexes, \\layer_names, \\layer_info, \\layer_name @/li\n"
"@li Log entries: \\each_log_entry @/li\n"
"@li Probing (get net from position): \\probe_net @/li\n"

View File

@ -42,22 +42,38 @@ module DRC
@engine._context("insert") do
requires_edges_or_region
args.each do |a|
if a.is_a?(RBA::DBox)
requires_edges_or_region
self.data.insert(RBA::Box::from_dbox(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DPolygon)
requires_edges_or_region
self.data.insert(RBA::Polygon::from_dpoly(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DSimplePolygon)
requires_edges_or_region
self.data.insert(RBA::SimplePolygon::from_dpoly(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::DPath)
requires_edges_or_region
self.data.insert(RBA::Path::from_dpath(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::Box) || a.is_a?(RBA::Polygon) || a.is_a?(RBA::SimplePolygon) || a.is_a?(RBA::Path)
requires_edges_or_region
self.data.insert(a)
elsif a.is_a?(RBA::DEdge)
requires_edges
self.data.insert(RBA::Edge::from_dedge(a * (1.0 / @engine.dbu)))
elsif a.is_a?(RBA::Edge)
requires_edges
self.data.insert(a)
elsif a.is_a?(RBA::DText)
requires_texts
self.data.insert(RBA::CplxTrans::new(@engine.dbu).inverted * a)
elsif a.is_a?(RBA::Text)
requires_texts
self.data.insert(a)
elsif a.is_a?(Array)
insert(*a)
else
raise("Invalid argument type for #{a.inspect}")
raise("Invalid argument type of #{a.inspect}")
end
end

View File

@ -1954,6 +1954,11 @@ TEST(122_NamedLayers)
compare_text_files (output, au_output);
}
TEST(123_DirectInsert)
{
run_test (_this, "123", false);
}
TEST(130_size_inside_outside)
{
run_test (_this, "130", false);

View File

@ -308,6 +308,25 @@ PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelI
} else {
if (m_index >= 0 && m_index < int (mp_properties_pages.size ()) && ! mp_properties_pages [m_index]->readonly ()) {
try {
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
mp_properties_pages [m_index]->apply ();
if (! t.is_empty ()) {
m_transaction_id = t.id ();
}
} catch (...) {
}
mp_properties_pages [m_index]->update ();
}
if (mp_tree_model->parent (index).isValid ()) {
m_index = mp_tree_model->page_index (index);
@ -343,9 +362,9 @@ PropertiesDialog::current_index_changed (const QModelIndex &index, const QModelI
m_object_indexes.push_back (oi);
}
} else {
} else if (mp_properties_pages [m_index]->count () > 0) {
m_object_indexes.push_back (size_t (mp_tree_model->object_index (index)));
m_object_indexes.push_back (0);
}

View File

@ -86,7 +86,7 @@ CIFReader::read (db::Layout &layout)
void
CIFReader::error (const std::string &msg)
{
throw CIFReaderException (msg, m_stream.line_number (), m_cellname);
throw CIFReaderException (msg, m_stream.line_number (), m_cellname, m_stream.source ());
}
void
@ -96,11 +96,19 @@ CIFReader::warn (const std::string &msg, int wl)
return;
}
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (" (line=")) << m_stream.line_number ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
tl::warn << msg
<< tl::to_string (tr (" (line=")) << m_stream.line_number ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}
/**

View File

@ -52,8 +52,8 @@ class DB_PLUGIN_PUBLIC CIFReaderException
: public ReaderException
{
public:
CIFReaderException (const std::string &msg, size_t l, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, cell=%s)")), msg, l, cell))
CIFReaderException (const std::string &msg, size_t l, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%ld, cell=%s), in file: %s")), msg, l, cell, source))
{ }
};

View File

@ -367,9 +367,9 @@ void
DXFReader::error (const std::string &msg)
{
if (m_ascii) {
throw DXFReaderException (msg, m_line_number, m_cellname);
throw DXFReaderException (msg, m_line_number, m_cellname, m_stream.source ());
} else {
throw DXFReaderException (msg, m_stream.pos (), m_cellname);
throw DXFReaderException (msg, m_stream.pos (), m_cellname, m_stream.source ());
}
}
@ -380,17 +380,25 @@ DXFReader::warn (const std::string &msg, int wl)
return;
}
// TODO: compress
if (m_ascii) {
tl::warn << msg
<< tl::to_string (tr (" (line=")) << m_line_number
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
} else {
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
if (m_ascii) {
tl::warn << msg
<< tl::to_string (tr (" (line=")) << m_line_number
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
} else {
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
}
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}

View File

@ -53,12 +53,12 @@ class DB_PLUGIN_PUBLIC DXFReaderException
: public ReaderException
{
public:
DXFReaderException (const std::string &msg, size_t p, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, cell=%s)")), msg.c_str (), p, cell))
DXFReaderException (const std::string &msg, size_t p, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, cell=%s), in file: %s")), msg.c_str (), p, cell, source))
{ }
DXFReaderException (const std::string &msg, int line, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%d, cell=%s)")), msg.c_str (), line, cell))
DXFReaderException (const std::string &msg, int line, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line=%d, cell=%s), in file: %s")), msg.c_str (), line, cell, source))
{ }
};

View File

@ -298,7 +298,7 @@ GDS2ReaderText::path () const
void
GDS2ReaderText::error (const std::string &msg)
{
throw GDS2ReaderTextException (msg, int(sStream.line_number()), cellname().c_str ());
throw GDS2ReaderTextException (msg, int (sStream.line_number()), cellname ().c_str (), sStream.source ());
}
void
@ -308,11 +308,19 @@ GDS2ReaderText::warn (const std::string &msg, int wl)
return;
}
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (", line number=")) << sStream.line_number()
<< tl::to_string (tr (", cell=")) << cellname ().c_str ()
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), sStream.source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
tl::warn << msg
<< tl::to_string (tr (", line number=")) << sStream.line_number()
<< tl::to_string (tr (", cell=")) << cellname ().c_str ()
<< ")";
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}
void

View File

@ -39,8 +39,8 @@ class DB_PLUGIN_PUBLIC GDS2ReaderTextException
: public ReaderException
{
public:
GDS2ReaderTextException (const std::string &msg, size_t n, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line number=%ld, cell=%s)")).c_str (), msg.c_str (), n, cell.c_str ()))
GDS2ReaderTextException (const std::string &msg, size_t n, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (line number=%ld, cell=%s), in file: %s")).c_str (), msg.c_str (), n, cell.c_str (), source))
{ }
};

View File

@ -282,7 +282,7 @@ GDS2Reader::path () const
void
GDS2Reader::error (const std::string &msg)
{
throw GDS2ReaderException (msg, m_stream.pos (), m_recnum, cellname ().c_str ());
throw GDS2ReaderException (msg, m_stream.pos (), m_recnum, cellname ().c_str (), m_stream.source ());
}
void
@ -292,12 +292,20 @@ GDS2Reader::warn (const std::string &msg, int wl)
return;
}
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", record number=")) << m_recnum
<< tl::to_string (tr (", cell=")) << cellname ().c_str ()
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", record number=")) << m_recnum
<< tl::to_string (tr (", cell=")) << cellname ().c_str ()
<< ")";
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}
}

View File

@ -48,8 +48,8 @@ class DB_PLUGIN_PUBLIC GDS2ReaderException
: public ReaderException
{
public:
GDS2ReaderException (const std::string &msg, size_t p, size_t n, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, record number=%ld, cell=%s)")), msg, p, n, cell))
GDS2ReaderException (const std::string &msg, size_t p, size_t n, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, record number=%ld, cell=%s), in file: %s")), msg, p, n, cell, source))
{ }
};

View File

@ -143,11 +143,19 @@ MAGReader::warn (const std::string &msg, int wl)
return;
}
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (" (line=")) << mp_current_stream->line_number ()
<< tl::to_string (tr (", file=")) << mp_current_stream->source ()
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), mp_current_stream->source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
tl::warn << msg
<< tl::to_string (tr (" (line=")) << mp_current_stream->line_number ()
<< tl::to_string (tr (", file=")) << mp_current_stream->source ()
<< ")";
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}
db::cell_index_type

View File

@ -482,7 +482,7 @@ OASISReader::get_gdelta (long grid)
void
OASISReader::error (const std::string &msg)
{
throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ());
throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str (), m_stream.source ());
}
void
@ -493,13 +493,25 @@ OASISReader::warn (const std::string &msg, int wl)
}
if (warnings_as_errors ()) {
error (msg);
} else {
// TODO: compress
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
if (first_warning ()) {
tl::warn << tl::sprintf (tl::to_string (tr ("In file %s:")), m_stream.source ());
}
int ws = compress_warning (msg);
if (ws < 0) {
tl::warn << msg
<< tl::to_string (tr (" (position=")) << m_stream.pos ()
<< tl::to_string (tr (", cell=")) << m_cellname
<< ")";
} else if (ws == 0) {
tl::warn << tl::to_string (tr ("... further warnings of this kind are not shown"));
}
}
}

View File

@ -54,8 +54,8 @@ class DB_PLUGIN_PUBLIC OASISReaderException
: public ReaderException
{
public:
OASISReaderException (const std::string &msg, size_t p, const std::string &cell)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, cell=%s)")), msg, p, cell))
OASISReaderException (const std::string &msg, size_t p, const std::string &cell, const std::string &source)
: ReaderException (tl::sprintf (tl::to_string (tr ("%s (position=%ld, cell=%s), in file: %s")), msg, p, cell, source))
{ }
};

View File

@ -127,7 +127,7 @@ run_test_error (tl::TestBase *_this, const char *test, const char *msg_au)
error = true;
}
EXPECT_EQ (error, true)
EXPECT_EQ (msg, msg_au)
EXPECT_EQ (msg.find (msg_au), size_t (0));
}
TEST(1_1)
@ -635,7 +635,7 @@ TEST(Bug_1474)
// Seen when private test data is not installed
throw;
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Cell named ADDHX2 with ID 4 was already given name SEDFFTRX2 (position=763169, cell=)");
EXPECT_EQ (ex.msg ().find ("Cell named ADDHX2 with ID 4 was already given name SEDFFTRX2 (position=763169, cell=)"), size_t (0));
}
}
@ -678,6 +678,6 @@ TEST(DuplicateCellname)
// Seen when private test data is not installed
throw;
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)");
EXPECT_EQ (ex.msg ().find ("Same cell name TOP, but different IDs: 3 and 0 (position=1070, cell=)"), size_t (0));
}
}

View File

@ -1178,20 +1178,34 @@ OutputStream::seek (size_t pos)
OutputFileBase::OutputFileBase (const std::string &p, int keep_backups)
: m_keep_backups (keep_backups), m_path (tl::absolute_file_path (p)), m_has_error (false)
{
if (p.empty ()) {
throw tl::Exception (tl::to_string (tr ("Path cannot be an empty string")));
}
if (tl::file_exists (m_path)) {
m_backup_path = m_path + ".~backup";
if (tl::file_exists (m_backup_path)) {
if (! tl::rm_file (m_backup_path)) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to remove existing file '%s'")), m_backup_path);
m_backup_path = std::string ();
if (tl::is_dir (m_path)) {
throw tl::Exception (tl::to_string (tr ("Path exists and is a directory: '%s'")), m_path);
} else {
m_backup_path = m_path + ".~backup";
if (tl::file_exists (m_backup_path)) {
if (! tl::rm_file (m_backup_path)) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to remove existing file '%s'")), m_backup_path);
m_backup_path = std::string ();
}
}
}
if (! m_backup_path.empty ()) {
if (! tl::rename_file (m_path, tl::filename (m_backup_path))) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), m_path, m_backup_path);
m_backup_path = std::string ();
if (! m_backup_path.empty ()) {
if (! tl::rename_file (m_path, tl::filename (m_backup_path))) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), m_path, m_backup_path);
m_backup_path = std::string ();
}
}
}
}
}

View File

@ -462,5 +462,20 @@ TEST(Backups)
}
}
TEST(RefuseToWrite)
{
try {
tl::OutputStream os ("");
EXPECT_EQ (1, 0);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Path cannot be an empty string");
}
try {
tl::OutputStream os (".");
EXPECT_EQ (1, 0);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg ().find ("Path exists and is a directory"), size_t (0));
}
}

132
testdata/drc/drcSimpleTests_123.drc vendored Normal file
View File

@ -0,0 +1,132 @@
source $drc_test_source
target $drc_test_target
lp = polygons
le = edges
ll = labels
lp.insert(RBA::Box::new(0, 0, 100, 200))
lp.insert(RBA::DBox::new(0.200, 0, 0.300, 0.200))
lp.insert(RBA::Polygon::new(RBA::Box::new(400, 0, 500, 200)))
lp.insert(RBA::DPolygon::new(RBA::DBox::new(0.600, 0, 0.700, 0.200)))
lp.insert(RBA::SimplePolygon::new(RBA::Box::new(800, 0, 900, 200)))
lp.insert(RBA::DSimplePolygon::new(RBA::DBox::new(1.000, 0, 1.100, 0.200)))
lp.insert(RBA::Path::new([ RBA::Point::new(1200, 100), RBA::Point::new(1300, 100) ], 200))
lp.insert(RBA::DPath::new([ RBA::DPoint::new(1.400, 0.1), RBA::DPoint::new(1.500, 0.1) ], 0.2))
begin
lp.insert(RBA::Edge::new(RBA::Point::new(0, 0), RBA::Point::new(100, 100)))
raise Exception::new("Error expected")
rescue => ex
end
begin
lp.insert(RBA::DEdge::new(RBA::DPoint::new(0, 0), RBA::DPoint::new(0.1, 0.1)))
raise Exception::new("Error expected")
rescue => ex
end
begin
lp.insert(RBA::Text::new("ABC", RBA::Trans::new(RBA::Vector::new(0, 0))))
raise Exception::new("Error expected")
rescue => ex
end
begin
lp.insert(RBA::DText::new("XYZ", RBA::DTrans::new(RBA::DVector::new(0, 0.1))))
raise Exception::new("Error expected")
rescue => ex
end
le.insert(RBA::Box::new(0, 0, 100, 200))
le.insert(RBA::DBox::new(0.200, 0, 0.300, 0.200))
le.insert(RBA::Polygon::new(RBA::Box::new(400, 0, 500, 200)))
le.insert(RBA::DPolygon::new(RBA::DBox::new(0.600, 0, 0.700, 0.200)))
le.insert(RBA::SimplePolygon::new(RBA::Box::new(800, 0, 900, 200)))
le.insert(RBA::DSimplePolygon::new(RBA::DBox::new(1.000, 0, 1.100, 0.200)))
le.insert(RBA::Path::new([ RBA::Point::new(1200, 100), RBA::Point::new(1300, 100) ], 200))
le.insert(RBA::DPath::new([ RBA::DPoint::new(1.400, 0.1), RBA::DPoint::new(1.500, 0.1) ], 0.2))
le.insert(RBA::Edge::new(RBA::Point::new(0, 0), RBA::Point::new(100, 100)))
le.insert(RBA::DEdge::new(RBA::DPoint::new(0, 0.1), RBA::DPoint::new(0.1, 0.2)))
begin
le.insert(RBA::Text::new("ABC", RBA::Trans::new(RBA::Vector::new(0, 0))))
raise Exception::new("Error expected")
rescue => ex
end
begin
le.insert(RBA::DText::new("XYZ", RBA::DTrans::new(RBA::DVector::new(0, 0.1))))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::Box::new(0, 0, 100, 200))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::DBox::new(0.200, 0, 0.300, 0.200))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::Polygon::new(RBA::Box::new(400, 0, 500, 200)))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::DPolygon::new(RBA::DBox::new(0.600, 0, 0.700, 0.200)))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::SimplePolygon::new(RBA::Box::new(800, 0, 900, 200)))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::DSimplePolygon::new(RBA::DBox::new(1.000, 0, 1.100, 0.200)))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::Path::new([ RBA::Point::new(1200, 100), RBA::Point::new(1300, 100) ], 200))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::DPath::new([ RBA::DPoint::new(1.400, 0.1), RBA::DPoint::new(1.500, 0.1) ], 0.2))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::Edge::new(RBA::Point::new(0, 0), RBA::Point::new(100, 100)))
raise Exception::new("Error expected")
rescue => ex
end
begin
ll.insert(RBA::DEdge::new(RBA::DPoint::new(0, 0.1), RBA::DPoint::new(0.1, 0.2)))
raise Exception::new("Error expected")
rescue => ex
end
ll.insert(RBA::Text::new("ABC", RBA::Trans::new(RBA::Vector::new(100, 0))))
ll.insert(RBA::DText::new("XYZ", RBA::DTrans::new(RBA::DVector::new(0, 0.1))))
lp.output(1, 0)
le.output(2, 0)
ll.output(3, 0)

BIN
testdata/drc/drcSimpleTests_123.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au123.gds vendored Normal file

Binary file not shown.

View File

@ -489,7 +489,8 @@ class DBLayoutTests2_TestClass < TestBase
def test_6_Layout_props
ly = RBA::Layout::new
pid = ly.properties_id( { 17 => "a", "b" => [ 1, 5, 7 ] }.to_a )
pid = ly.properties_id({ 17 => "a", "b" => [ 1, 5, 7 ] }.to_a)
assert_equal(ly.properties_id({ 17 => "a", "b" => [ 1, 5, 7 ] }), pid)
ci1 = ly.add_cell( "c1" )
ci2 = ly.add_cell( "c2" )
@ -1051,6 +1052,10 @@ class DBLayoutTests2_TestClass < TestBase
assert_equal(ly.prop_id, 1)
assert_equal(ly.property("x"), 1)
assert_equal(ly.properties, {"x" => 1})
assert_equal(ly.properties_hash(ly.prop_id), {"x" => 1})
assert_equal(ly.properties_id(ly.properties_hash(ly.prop_id)), ly.prop_id)
assert_equal(ly.properties_array(ly.prop_id), [["x", 1]])
assert_equal(ly.properties_id(ly.properties_array(ly.prop_id)), ly.prop_id)
ly.set_property("x", 17)
assert_equal(ly.prop_id, 2)
assert_equal(ly.property("x"), 17)