mirror of https://github.com/KLayout/klayout.git
commit
248168ea67
|
|
@ -506,7 +506,7 @@ Cell::collect_caller_cells (std::set<cell_index_type> &callers, const std::set<c
|
|||
{
|
||||
if (levels != 0) {
|
||||
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
|
||||
if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end ()) {
|
||||
if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end () && mp_layout->is_valid_cell_index (*cc)) {
|
||||
callers.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
|
|
@ -519,7 +519,7 @@ Cell::collect_caller_cells (std::set<cell_index_type> &callers, int levels) cons
|
|||
{
|
||||
if (levels != 0) {
|
||||
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
|
||||
if (callers.find (*cc) == callers.end ()) {
|
||||
if (callers.find (*cc) == callers.end () && mp_layout->is_valid_cell_index (*cc)) {
|
||||
callers.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
|
|
@ -538,7 +538,7 @@ Cell::collect_called_cells (std::set<cell_index_type> &called, int levels) const
|
|||
{
|
||||
if (levels != 0) {
|
||||
for (child_cell_iterator cc = begin_child_cells (); ! cc.at_end (); ++cc) {
|
||||
if (called.find (*cc) == called.end ()) {
|
||||
if (called.find (*cc) == called.end () && mp_layout->is_valid_cell_index (*cc)) {
|
||||
called.insert (*cc);
|
||||
mp_layout->cell (*cc).collect_called_cells (called, levels < 0 ? levels : levels - 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,361 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Common reader implementation
|
||||
|
||||
static const size_t null_id = std::numeric_limits<size_t>::max ();
|
||||
|
||||
CommonReader::CommonReader ()
|
||||
: m_cc_resolution (AddToCell)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
CommonReader::make_cell (db::Layout &layout, const std::string &cn)
|
||||
{
|
||||
tl_assert (! cn.empty ());
|
||||
|
||||
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
||||
if (iname != m_name_map.end ()) {
|
||||
|
||||
db::Cell &cell = layout.cell (iname->second.second);
|
||||
|
||||
if (! cell.is_ghost_cell ()) {
|
||||
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with name %s already exists")), cn));
|
||||
}
|
||||
|
||||
cell.set_ghost_cell (false);
|
||||
return cell.cell_index ();
|
||||
|
||||
} else {
|
||||
|
||||
db::cell_index_type ci = layout.add_anonymous_cell ();
|
||||
|
||||
m_name_map [cn] = std::make_pair (null_id, ci);
|
||||
return ci;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CommonReader::has_cell (const std::string &cn) const
|
||||
{
|
||||
return m_name_map.find (cn) != m_name_map.end ();
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type>
|
||||
CommonReader::cell_by_name (const std::string &cn) const
|
||||
{
|
||||
std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator iname = m_name_map.find (cn);
|
||||
if (iname != m_name_map.end ()) {
|
||||
return std::make_pair (true, iname->second.second);
|
||||
} else {
|
||||
return std::make_pair (false, size_t (0));
|
||||
}
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
CommonReader::make_cell (db::Layout &layout, size_t id)
|
||||
{
|
||||
tl_assert (id != null_id);
|
||||
|
||||
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
||||
if (iid != m_id_map.end ()) {
|
||||
|
||||
db::Cell &cell = layout.cell (iid->second.second);
|
||||
|
||||
if (! cell.is_ghost_cell ()) {
|
||||
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with ID %ld already exists")), id));
|
||||
}
|
||||
|
||||
cell.set_ghost_cell (false);
|
||||
return cell.cell_index ();
|
||||
|
||||
} else {
|
||||
|
||||
db::cell_index_type ci = layout.add_anonymous_cell ();
|
||||
|
||||
m_id_map [id] = std::make_pair (std::string (), ci);
|
||||
return ci;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CommonReader::has_cell (size_t id) const
|
||||
{
|
||||
return m_id_map.find (id) != m_id_map.end ();
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type>
|
||||
CommonReader::cell_by_id (size_t id) const
|
||||
{
|
||||
std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator iid = m_id_map.find (id);
|
||||
if (iid != m_id_map.end ()) {
|
||||
return std::make_pair (true, iid->second.second);
|
||||
} else {
|
||||
return std::make_pair (false, size_t (0));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &
|
||||
CommonReader::name_for_id (size_t id) const
|
||||
{
|
||||
std::map<size_t, std::string>::const_iterator n = m_name_for_id.find (id);
|
||||
if (n != m_name_for_id.end ()) {
|
||||
return n->second;
|
||||
} else {
|
||||
static std::string empty;
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CommonReader::rename_cell (db::Layout &layout, size_t id, const std::string &cn)
|
||||
{
|
||||
m_name_for_id.insert (std::make_pair (id, cn));
|
||||
|
||||
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
||||
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
||||
|
||||
if (iid != m_id_map.end () && iname != m_name_map.end ()) {
|
||||
|
||||
if (! iid->second.first.empty () && iid->second.first != cn) {
|
||||
common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first));
|
||||
}
|
||||
|
||||
if (iname->second.second != iid->second.second) {
|
||||
|
||||
// Both cells already exist and are not identical: merge ID-declared cell into the name-declared one
|
||||
layout.force_update ();
|
||||
merge_cell (layout, iname->second.second, iid->second.second);
|
||||
iid->second.second = iname->second.second;
|
||||
|
||||
}
|
||||
|
||||
iid->second.first = cn;
|
||||
iname->second.first = id;
|
||||
|
||||
} else if (iid != m_id_map.end ()) {
|
||||
|
||||
m_name_map [cn] = std::make_pair (id, iid->second.second);
|
||||
iid->second.first = cn;
|
||||
|
||||
} else if (iname != m_name_map.end ()) {
|
||||
|
||||
m_id_map [id] = std::make_pair (cn, iname->second.second);
|
||||
iname->second.first = id;
|
||||
|
||||
} else {
|
||||
|
||||
db::cell_index_type ci = layout.add_anonymous_cell ();
|
||||
layout.cell (ci).set_ghost_cell (true);
|
||||
|
||||
m_id_map [id] = std::make_pair (cn, ci);
|
||||
m_name_map [cn] = std::make_pair (id, ci);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
CommonReader::cell_for_instance (db::Layout &layout, size_t id)
|
||||
{
|
||||
tl_assert (id != null_id);
|
||||
|
||||
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
|
||||
if (iid != m_id_map.end ()) {
|
||||
|
||||
return iid->second.second;
|
||||
|
||||
} else {
|
||||
|
||||
db::cell_index_type ci = layout.add_anonymous_cell ();
|
||||
layout.cell (ci).set_ghost_cell (true);
|
||||
|
||||
m_id_map [id] = std::make_pair (std::string (), ci);
|
||||
return ci;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
CommonReader::cell_for_instance (db::Layout &layout, const std::string &cn)
|
||||
{
|
||||
tl_assert (! cn.empty ());
|
||||
|
||||
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
|
||||
if (iname != m_name_map.end ()) {
|
||||
|
||||
return iname->second.second;
|
||||
|
||||
} else {
|
||||
|
||||
db::cell_index_type ci = layout.add_anonymous_cell ();
|
||||
layout.cell (ci).set_ghost_cell (true);
|
||||
|
||||
m_name_map [cn] = std::make_pair (null_id, ci);
|
||||
return ci;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CommonReader::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const
|
||||
{
|
||||
const db::Cell &src_cell = layout.cell (src_cell_index);
|
||||
db::Cell &target_cell = layout.cell (target_cell_index);
|
||||
|
||||
// copy over the instances
|
||||
for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i) {
|
||||
// NOTE: cell indexed may be invalid because we delete subcells without update()
|
||||
if (layout.is_valid_cell_index (i->cell_index ())) {
|
||||
target_cell.insert (*i);
|
||||
}
|
||||
}
|
||||
|
||||
merge_cell_without_instances (layout, target_cell_index, src_cell_index);
|
||||
}
|
||||
|
||||
void
|
||||
CommonReader::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const
|
||||
{
|
||||
const db::Cell &src_cell = layout.cell (src_cell_index);
|
||||
db::Cell &target_cell = layout.cell (target_cell_index);
|
||||
|
||||
// copy over the shapes
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
if (layout.is_valid_layer (l) && ! src_cell.shapes (l).empty ()) {
|
||||
target_cell.shapes (l).insert (src_cell.shapes (l));
|
||||
}
|
||||
}
|
||||
|
||||
// replace all instances of the new cell with the original one
|
||||
std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
|
||||
for (db::Cell::parent_inst_iterator pi = src_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
|
||||
}
|
||||
|
||||
for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
|
||||
db::CellInstArray ia = p->second.cell_inst ();
|
||||
ia.object ().cell_index (target_cell.cell_index ());
|
||||
layout.cell (p->first).replace (p->second, ia);
|
||||
}
|
||||
|
||||
// finally delete the new cell
|
||||
layout.delete_cell (src_cell.cell_index ());
|
||||
}
|
||||
|
||||
void
|
||||
CommonReader::finish (db::Layout &layout)
|
||||
{
|
||||
bool any_missing = false;
|
||||
|
||||
for (std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator i = m_id_map.begin (); i != m_id_map.end (); ++i) {
|
||||
if (i->second.first.empty ()) {
|
||||
common_reader_warn (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), i->first));
|
||||
any_missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_missing) {
|
||||
common_reader_error (tl::to_string (tr ("Some cell IDs don't have a name (see previous warnings)")));
|
||||
}
|
||||
|
||||
// check if we need to resolve conflicts
|
||||
|
||||
bool has_conflict = false;
|
||||
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) {
|
||||
has_conflict = layout.cell_by_name (i->first.c_str ()).first;
|
||||
}
|
||||
|
||||
if (! has_conflict) {
|
||||
|
||||
// no conflict - plain rename
|
||||
|
||||
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) {
|
||||
layout.rename_cell (i->second.second, i->first.c_str ());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// elaborate conflict resolution
|
||||
|
||||
layout.force_update ();
|
||||
|
||||
std::map<db::cell_index_type, std::string> new_cells;
|
||||
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end (); ++i) {
|
||||
new_cells.insert (std::make_pair (i->second.second, i->first));
|
||||
}
|
||||
|
||||
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > cells_with_conflict;
|
||||
|
||||
// First treat all the cells without conflict
|
||||
for (db::Layout::bottom_up_iterator bu = layout.begin_bottom_up (); bu != layout.end_bottom_up (); ++bu) {
|
||||
|
||||
db::cell_index_type ci_new = *bu;
|
||||
std::map<db::cell_index_type, std::string>::const_iterator i = new_cells.find (ci_new);
|
||||
|
||||
if (i == new_cells.end ()) {
|
||||
// not a new cell
|
||||
continue;
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type> c2n = layout.cell_by_name (i->second.c_str ());
|
||||
db::cell_index_type ci_org = c2n.second;
|
||||
|
||||
// NOTE: proxy cells are never resolved. "RenameCell" is a plain and simple case.
|
||||
if (c2n.first && m_cc_resolution != RenameCell && ! layout.cell (ci_org).is_proxy ()) {
|
||||
cells_with_conflict.push_back (std::make_pair (ci_new, ci_org));
|
||||
} else {
|
||||
layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Then treat all the cells with conflict
|
||||
for (std::vector<std::pair<db::cell_index_type, db::cell_index_type> >::const_iterator cc = cells_with_conflict.begin (); cc != cells_with_conflict.end (); ++cc) {
|
||||
|
||||
db::cell_index_type ci_new = cc->first;
|
||||
db::cell_index_type ci_org = cc->second;
|
||||
|
||||
// we have a cell conflict
|
||||
|
||||
if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) {
|
||||
|
||||
if (! layout.cell (ci_org).begin ().at_end ()) {
|
||||
|
||||
// NOTE: because prune_subcells needs the parents for sub cells and we are going do delete
|
||||
// the current cell, we cannot save the "update()" just by traversing bottom-up.
|
||||
layout.force_update ();
|
||||
layout.prune_subcells (ci_org);
|
||||
|
||||
}
|
||||
|
||||
layout.cell (ci_org).clear_shapes ();
|
||||
|
||||
merge_cell (layout, ci_org, ci_new);
|
||||
|
||||
} else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) {
|
||||
|
||||
layout.prune_subcells (ci_new);
|
||||
layout.cell (ci_new).clear_shapes ();
|
||||
|
||||
// NOTE: ignore instances -> this saves us a layout update
|
||||
merge_cell_without_instances (layout, ci_org, ci_new);
|
||||
|
||||
} else {
|
||||
|
||||
merge_cell (layout, ci_org, ci_new);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Common format declaration
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,128 @@
|
|||
namespace db
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A common reader base for GDS2 and OASIS providing common services for both readers
|
||||
*/
|
||||
class DB_PUBLIC CommonReader
|
||||
: public ReaderBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The CellConflictResolution enum
|
||||
*/
|
||||
enum CellConflictResolution
|
||||
{
|
||||
AddToCell = 0,
|
||||
OverwriteCell = 1,
|
||||
SkipNewCell = 2,
|
||||
RenameCell = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
CommonReader ();
|
||||
|
||||
/**
|
||||
* @brief Sets the cell name conflict resolution mode
|
||||
*/
|
||||
void set_cell_conflict_resolution (CellConflictResolution cc_resolution)
|
||||
{
|
||||
m_cc_resolution = cc_resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the cell name conflict resolution mode
|
||||
*/
|
||||
CellConflictResolution cell_conflict_resolution () const
|
||||
{
|
||||
return m_cc_resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a cell from a name
|
||||
*/
|
||||
db::cell_index_type make_cell (db::Layout &layout, const std::string &cn);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if there is a cell with the given name already
|
||||
*/
|
||||
bool has_cell (const std::string &cn) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a pair with a bool (indicating whether the cell name is known) and the cell index for this name
|
||||
*/
|
||||
std::pair<bool, db::cell_index_type> cell_by_name (const std::string &name) const;
|
||||
|
||||
/**
|
||||
* @brief Make a cell from an ID (OASIS)
|
||||
*/
|
||||
db::cell_index_type make_cell (db::Layout &layout, size_t id);
|
||||
|
||||
/**
|
||||
* @brief Returns true, if there is a cell with the given ID alreay
|
||||
*/
|
||||
bool has_cell (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a pair with a bool (indicating whether the cell ID is known) and the cell index for this ID
|
||||
*/
|
||||
std::pair<bool, db::cell_index_type> cell_by_id (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Registers a cell name for an ID
|
||||
*/
|
||||
void rename_cell (db::Layout &layout, size_t id, const std::string &cn);
|
||||
|
||||
/**
|
||||
* @brief Gets the name for a given cell ID if known, otherwise returns an empty string
|
||||
*/
|
||||
const std::string &name_for_id (size_t id) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a cell reference by ID
|
||||
* If the cell does not exist, it's created. It is marked as ghost cell until
|
||||
* "make_cell" is called.
|
||||
*/
|
||||
db::cell_index_type cell_for_instance (db::Layout &layout, size_t id);
|
||||
|
||||
/**
|
||||
* @brief Returns a cell reference by name
|
||||
* Same as the previous method, but acting on cell names.
|
||||
*/
|
||||
db::cell_index_type cell_for_instance (db::Layout &layout, const std::string &cn);
|
||||
|
||||
/**
|
||||
* @brief Finishes the reading process
|
||||
*
|
||||
* This method will first check if all cells IDs got a name.
|
||||
* After this, the cells are renamed and cell conflict resolution will happen in the
|
||||
* specified way (cell_conflict_resolution attribute).
|
||||
*/
|
||||
void finish (db::Layout &layout);
|
||||
|
||||
protected:
|
||||
virtual void common_reader_error (const std::string &msg) = 0;
|
||||
virtual void common_reader_warn (const std::string &msg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Merge (and delete) the src_cell into target_cell
|
||||
*/
|
||||
void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const;
|
||||
|
||||
/**
|
||||
* @brief Merge (and delete) the src_cell into target_cell without instances
|
||||
*/
|
||||
void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const;
|
||||
|
||||
private:
|
||||
std::map<size_t, std::pair<std::string, db::cell_index_type> > m_id_map;
|
||||
std::map<std::string, std::pair<size_t, db::cell_index_type> > m_name_map;
|
||||
std::map<size_t, std::string> m_name_for_id;
|
||||
CellConflictResolution m_cc_resolution;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure that holds the GDS2 and OASIS specific options for the reader
|
||||
*/
|
||||
|
|
@ -44,7 +166,8 @@ public:
|
|||
CommonReaderOptions ()
|
||||
: create_other_layers (true),
|
||||
enable_text_objects (true),
|
||||
enable_properties (true)
|
||||
enable_properties (true),
|
||||
cell_conflict_resolution (CommonReader::AddToCell)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -83,6 +206,22 @@ public:
|
|||
*/
|
||||
bool enable_properties;
|
||||
|
||||
/**
|
||||
* @brief Specifies the cell merge behavior
|
||||
*
|
||||
* This enum controls how cells are read if a cell with the requested name already
|
||||
* exists.
|
||||
*
|
||||
* AddToCell In this mode, instances or shapes are added to any existing cell
|
||||
* OverwriteCell Overwrite existing cell. If the existing cell has children, those are removed unless used otherwise
|
||||
* SkipNewCell Ignore the new cell and it's children
|
||||
* RenameCell Rename the new cell
|
||||
*
|
||||
* If the existing opr the new cell is a ghost cell, AddToCell is applied always. In other words,
|
||||
* ghost cells are always merged.
|
||||
*/
|
||||
CommonReader::CellConflictResolution cell_conflict_resolution;
|
||||
|
||||
/**
|
||||
* @brief Implementation of FormatSpecificReaderOptions
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1068,14 +1068,41 @@ Layout::add_cell (const char *name)
|
|||
return new_index;
|
||||
}
|
||||
|
||||
cell_index_type
|
||||
Layout::add_anonymous_cell ()
|
||||
{
|
||||
std::string b;
|
||||
|
||||
// create a new cell
|
||||
cell_index_type new_index = allocate_new_cell ();
|
||||
|
||||
cell_type *new_cell = new cell_type (new_index, *this);
|
||||
m_cells.push_back_ptr (new_cell);
|
||||
m_cell_ptrs [new_index] = new_cell;
|
||||
|
||||
// enter it's index and cell_name
|
||||
register_cell_name (0, new_index);
|
||||
|
||||
if (manager () && manager ()->transacting ()) {
|
||||
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
|
||||
}
|
||||
|
||||
return new_index;
|
||||
}
|
||||
|
||||
void
|
||||
Layout::register_cell_name (const char *name, cell_index_type ci)
|
||||
{
|
||||
// enter it's index and cell_name
|
||||
char *cp;
|
||||
|
||||
cp = new char [strlen (name) + 1];
|
||||
strcpy (cp, name);
|
||||
if (name == 0) {
|
||||
cp = new char [1];
|
||||
*cp = 0;
|
||||
} else {
|
||||
cp = new char [strlen (name) + 1];
|
||||
strcpy (cp, name);
|
||||
}
|
||||
|
||||
while (m_cell_names.size () < ci) {
|
||||
char *e = new char [1];
|
||||
|
|
@ -1090,7 +1117,9 @@ Layout::register_cell_name (const char *name, cell_index_type ci)
|
|||
m_cell_names.push_back (cp);
|
||||
}
|
||||
|
||||
m_cell_map.insert (std::make_pair (cp, ci));
|
||||
if (name) {
|
||||
m_cell_map.insert (std::make_pair (cp, ci));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -727,7 +727,17 @@ public:
|
|||
*/
|
||||
cell_index_type add_cell (const char *name = 0);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Add a cell without a name
|
||||
*
|
||||
* The cell is created, but cannot be found by name. The name returned is an empty string.
|
||||
* The cell is created with the purpose of being renamed later.
|
||||
*
|
||||
* @return The index of the new cell
|
||||
*/
|
||||
cell_index_type add_anonymous_cell ();
|
||||
|
||||
/**
|
||||
* @brief Rename a cell
|
||||
*
|
||||
* Rename the cell with the given id.
|
||||
|
|
@ -1065,8 +1075,7 @@ public:
|
|||
* @brief Delete the subcells of the given cells which are not used otherwise
|
||||
*
|
||||
* All subcells referenced directy or indirectly but not used otherwise
|
||||
* are deleted as well. This basically prunes the cell tree by this cell.
|
||||
* All instances of this cell are deleted as well.
|
||||
* are deleted as well.
|
||||
* This method is more efficent than calling prune_subcells for single cells multiple times.
|
||||
*
|
||||
* @param from A begin iterator delivering the cell id's to delete
|
||||
|
|
@ -1085,8 +1094,7 @@ public:
|
|||
* @brief Delete the subcells of the given cells which are not used otherwise
|
||||
*
|
||||
* All subcells referenced directy or indirectly but not used otherwise
|
||||
* are deleted as well. This basically prunes the cell tree by this cell.
|
||||
* All instances of this cell are deleted as well.
|
||||
* are deleted as well.
|
||||
* This method is more efficent than calling prune_subcells for single cells multiple times.
|
||||
*
|
||||
* @param cells A set of cell id's to prune
|
||||
|
|
|
|||
|
|
@ -174,9 +174,15 @@ Shapes::do_insert (const Shapes &d)
|
|||
if (layout () == d.layout ()) {
|
||||
|
||||
// both shape containers reside in the same repository space - simply copy
|
||||
m_layers.reserve (d.m_layers.size ());
|
||||
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
|
||||
m_layers.push_back ((*l)->clone (this, manager ()));
|
||||
if (m_layers.empty ()) {
|
||||
m_layers.reserve (d.m_layers.size ());
|
||||
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
|
||||
m_layers.push_back ((*l)->clone (this, manager ()));
|
||||
}
|
||||
} else {
|
||||
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
|
||||
(*l)->insert_into (this);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (layout () == 0) {
|
||||
|
|
|
|||
|
|
@ -493,6 +493,7 @@ public:
|
|||
virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0;
|
||||
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const = 0;
|
||||
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0;
|
||||
virtual void insert_into (Shapes *target) = 0;
|
||||
virtual void deref_into (Shapes *target) = 0;
|
||||
virtual void deref_into (Shapes *target, pm_delegate_type &pm) = 0;
|
||||
virtual void deref_and_transform_into (Shapes *target, const Trans &trans) = 0;
|
||||
|
|
|
|||
|
|
@ -834,6 +834,13 @@ layer_class<Sh, StableTag>::transform_into (Shapes *target, const ICplxTrans &tr
|
|||
}
|
||||
}
|
||||
|
||||
template <class Sh, class StableTag>
|
||||
void
|
||||
layer_class<Sh, StableTag>::insert_into (Shapes *target)
|
||||
{
|
||||
target->insert (m_layer.begin (), m_layer.end ());
|
||||
}
|
||||
|
||||
template <class Sh, class StableTag>
|
||||
void
|
||||
layer_class<Sh, StableTag>::deref_into (Shapes *target)
|
||||
|
|
@ -870,6 +877,7 @@ layer_class<Sh, StableTag>::deref_and_transform_into (Shapes *target, const Tran
|
|||
{
|
||||
deref_and_transform_into_shapes deref_op (target);
|
||||
for (typename layer_type::iterator s = m_layer.begin (); s != m_layer.end (); ++s) {
|
||||
|
||||
deref_op (*s, trans, pm);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ public:
|
|||
virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const;
|
||||
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const;
|
||||
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const;
|
||||
virtual void insert_into (Shapes *target);
|
||||
virtual void deref_into (Shapes *target);
|
||||
virtual void deref_into (Shapes *target, pm_delegate_type &pm);
|
||||
virtual void deref_and_transform_into (Shapes *target, const Trans &trans);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "dbCommonReader.h"
|
||||
#include "dbLoadLayoutOptions.h"
|
||||
#include "gsiDecl.h"
|
||||
#include "gsiEnums.h"
|
||||
|
||||
namespace dn
|
||||
{
|
||||
|
|
@ -84,6 +85,16 @@ static void set_properties_enabled (db::LoadLayoutOptions *options, bool l)
|
|||
options->get_options<db::CommonReaderOptions> ().enable_properties = l;
|
||||
}
|
||||
|
||||
static db::CommonReader::CellConflictResolution get_cell_conflict_resolution (const db::LoadLayoutOptions *options)
|
||||
{
|
||||
return options->get_options<db::CommonReaderOptions> ().cell_conflict_resolution;
|
||||
}
|
||||
|
||||
static void set_cell_conflict_resolution (db::LoadLayoutOptions *options, db::CommonReader::CellConflictResolution cc)
|
||||
{
|
||||
options->get_options<db::CommonReaderOptions> ().cell_conflict_resolution = cc;
|
||||
}
|
||||
|
||||
// extend lay::LoadLayoutOptions with the Common options
|
||||
static
|
||||
gsi::ClassExt<db::LoadLayoutOptions> common_reader_options (
|
||||
|
|
@ -156,10 +167,56 @@ gsi::ClassExt<db::LoadLayoutOptions> common_reader_options (
|
|||
"@param enabled True, if properties should be read."
|
||||
"\n"
|
||||
"Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration."
|
||||
) +
|
||||
gsi::method_ext ("cell_conflict_resolution", &get_cell_conflict_resolution,
|
||||
"@brief Gets the cell conflict resolution mode\n"
|
||||
"\n"
|
||||
"Multiple layout files can be collected into a single Layout object by reading file after file into the Layout object. "
|
||||
"Cells with same names are considered a conflict. This mode indicates how such conflicts are resolved. See \\LoadLayoutOptions::CellConflictResolution "
|
||||
"for the values allowed. The default mode is \\LoadLayoutOptions::CellConflictResolution#AddToCell.\n"
|
||||
"\n"
|
||||
"This option has been introduced in version 0.27."
|
||||
) +
|
||||
gsi::method_ext ("cell_conflict_resolution=", &set_cell_conflict_resolution, gsi::arg ("mode"),
|
||||
"@brief Sets the cell conflict resolution mode\n"
|
||||
"\n"
|
||||
"See \\cell_conflict_resolution for details about this option.\n"
|
||||
"\n"
|
||||
"This option has been introduced in version 0.27."
|
||||
),
|
||||
""
|
||||
);
|
||||
|
||||
|
||||
gsi::EnumIn<db::LoadLayoutOptions, db::CommonReader::CellConflictResolution> decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution",
|
||||
gsi::enum_const ("AddToCell", db::CommonReader::AddToCell,
|
||||
"@brief Add content to existing cell\n"
|
||||
"This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name."
|
||||
) +
|
||||
gsi::enum_const ("OverwriteCell", db::CommonReader::OverwriteCell,
|
||||
"@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n"
|
||||
) +
|
||||
gsi::enum_const ("SkipNewCell", db::CommonReader::SkipNewCell,
|
||||
"@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n"
|
||||
) +
|
||||
gsi::enum_const ("RenameCell", db::CommonReader::RenameCell,
|
||||
"@brief The new cell will be renamed to become unique\n"
|
||||
),
|
||||
"@brief This enum specifies how cell conflicts are handled if a layout read into another layout and a cell name conflict arises. "
|
||||
"Until version 0.26.8 and before, the mode was always 'AddToCell'. On reading, a cell was 'reopened' when encountering a cell name "
|
||||
"which already existed. This mode is still the default. The other modes are made available to support other ways of merging layouts.\n"
|
||||
"\n"
|
||||
"Proxy cells are never modified in the existing layout. Proxy cells are always local to their layout file. So if the existing cell is "
|
||||
"a proxy cell, the new cell will be renamed.\n"
|
||||
"\n"
|
||||
"If the new or existing cell is a ghost cell, both cells are merged always.\n"
|
||||
"\n"
|
||||
"This enum was introduced in version 0.27.\n"
|
||||
);
|
||||
|
||||
// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference:
|
||||
gsi::ClassExt<db::LoadLayoutOptions> inject_CellConflictResolution_in_parent (decl_dbCommonReader_CellConflictResolution.defs ());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -460,8 +460,8 @@ TEST(BasicAnd9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -476,8 +476,8 @@ TEST(BasicNot9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -600,8 +600,8 @@ TEST(BasicAndWithSize9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -616,8 +616,8 @@ TEST(BasicNotWithSize9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -752,8 +752,8 @@ TEST(TwoInputsAnd9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -768,8 +768,8 @@ TEST(TwoInputsNot9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -892,8 +892,8 @@ TEST(TwoInputsAndWithSize9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -908,8 +908,8 @@ TEST(TwoInputsNotWithSize9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
|
||||
"RING[1] 1 insts, 0 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -984,8 +984,8 @@ TEST(BasicSelfOverlap9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 1 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 1 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1054,8 +1054,8 @@ TEST(BasicSelfOverlapWithSize9)
|
|||
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
|
||||
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
|
||||
"TOP[1] 0 insts, 0 shapes (1 times)\n"
|
||||
"RING[1] 0 insts, 1 shapes (1 times)\n"
|
||||
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
|
||||
"RING[1] 0 insts, 1 shapes (1 times)\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -318,6 +318,55 @@ real_class (const gsi::ClassBase *cls)
|
|||
return cls->declaration () ? cls->declaration () : cls;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class RecursiveClassIterator
|
||||
{
|
||||
public:
|
||||
typedef const gsi::ClassBase &reference;
|
||||
typedef const gsi::ClassBase *pointer;
|
||||
|
||||
RecursiveClassIterator ()
|
||||
{
|
||||
if (gsi::ClassBase::begin_classes () != gsi::ClassBase::end_classes ()) {
|
||||
m_cls_iter_stack.push_back (std::make_pair (gsi::ClassBase::begin_classes (), gsi::ClassBase::end_classes ()));
|
||||
}
|
||||
}
|
||||
|
||||
bool at_end () const
|
||||
{
|
||||
return m_cls_iter_stack.empty ();
|
||||
}
|
||||
|
||||
RecursiveClassIterator &operator++ ()
|
||||
{
|
||||
if (operator* ().begin_child_classes () != operator* ().end_child_classes ()) {
|
||||
m_cls_iter_stack.push_back (std::make_pair (operator* ().begin_child_classes (), operator* ().end_child_classes ()));
|
||||
} else {
|
||||
while (! m_cls_iter_stack.empty () && ++m_cls_iter_stack.back ().first == m_cls_iter_stack.back ().second) {
|
||||
m_cls_iter_stack.pop_back ();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const gsi::ClassBase &operator* () const
|
||||
{
|
||||
return *m_cls_iter_stack.back ().first;
|
||||
}
|
||||
|
||||
const gsi::ClassBase *operator-> () const
|
||||
{
|
||||
return m_cls_iter_stack.back ().first.operator-> ();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<std::pair<gsi::ClassBase::class_iterator, gsi::ClassBase::class_iterator> > m_cls_iter_stack;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static std::string
|
||||
replace_references (const std::string &t, const gsi::ClassBase *cls_base)
|
||||
{
|
||||
|
|
@ -341,7 +390,7 @@ replace_references (const std::string &t, const gsi::ClassBase *cls_base)
|
|||
r += std::string (t, q, p - q);
|
||||
|
||||
size_t pp = ++p;
|
||||
while (p < t.size () && (t[p] == '_' || isalnum (t [p]))) {
|
||||
while (p < t.size () && (t[p] == '_' || t[p] == ':' || isalnum (t [p]))) {
|
||||
++p;
|
||||
}
|
||||
if (p < t.size () && (t[p] == '?' || t [p] == '=')) {
|
||||
|
|
@ -369,8 +418,8 @@ replace_references (const std::string &t, const gsi::ClassBase *cls_base)
|
|||
found = true;
|
||||
}
|
||||
|
||||
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes () && !found; ++c) {
|
||||
if (c->name () == id) {
|
||||
for (RecursiveClassIterator c; ! c.at_end (); ++c) {
|
||||
if (c->qname () == id) {
|
||||
r += "<a href=\"";
|
||||
if (mid.empty ()) {
|
||||
r += escape_xml (class_doc_url (id));
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ GDS2ReaderText::read (db::Layout &layout, const db::LoadLayoutOptions &options)
|
|||
db::GDS2ReaderOptions gds2_options = options.get_options<db::GDS2ReaderOptions> ();
|
||||
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();
|
||||
|
||||
return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode);
|
||||
return basic_read (layout, common_options.layer_map, common_options.create_other_layers, common_options.enable_text_objects, common_options.enable_properties, false, gds2_options.box_mode, common_options.cell_conflict_resolution);
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
|
|
@ -259,7 +259,7 @@ GDS2ReaderText::get_double ()
|
|||
}
|
||||
|
||||
void
|
||||
GDS2ReaderText::get_string (tl::string &s) const
|
||||
GDS2ReaderText::get_string (std::string &s) const
|
||||
{
|
||||
// TODO: get rid of this const_cast hack
|
||||
s = (const_cast<GDS2ReaderText *> (this))->reader.skip ();
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ private:
|
|||
|
||||
virtual std::string path () const;
|
||||
const char *get_string ();
|
||||
void get_string (tl::string &s) const;
|
||||
void get_string (std::string &s) const;
|
||||
int get_int ();
|
||||
short get_short ();
|
||||
unsigned short get_ushort ();
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ GDS2Reader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
|
|||
--m_recnum;
|
||||
m_reclen = 0;
|
||||
|
||||
return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode);
|
||||
return basic_read (layout, m_common_options.layer_map, m_common_options.create_other_layers, m_common_options.enable_text_objects, m_common_options.enable_properties, m_options.allow_multi_xy_records, m_options.box_mode, m_common_options.cell_conflict_resolution);
|
||||
}
|
||||
|
||||
const LayerMap &
|
||||
|
|
@ -210,7 +210,7 @@ GDS2Reader::get_string ()
|
|||
}
|
||||
|
||||
void
|
||||
GDS2Reader::get_string (tl::string &s) const
|
||||
GDS2Reader::get_string (std::string &s) const
|
||||
{
|
||||
s.assign ((const char *) mp_rec_buf, 0, m_reclen);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ private:
|
|||
|
||||
virtual std::string path () const;
|
||||
virtual const char *get_string ();
|
||||
virtual void get_string (tl::string &s) const;
|
||||
virtual void get_string (std::string &s) const;
|
||||
virtual int get_int ();
|
||||
virtual short get_short ();
|
||||
virtual unsigned short get_ushort ();
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ GDS2ReaderBase::~GDS2ReaderBase ()
|
|||
}
|
||||
|
||||
const LayerMap &
|
||||
GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode)
|
||||
GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution)
|
||||
{
|
||||
m_layer_map = layer_map;
|
||||
m_layer_map.prepare (layout);
|
||||
|
|
@ -95,9 +95,17 @@ GDS2ReaderBase::basic_read (db::Layout &layout, const LayerMap &layer_map, bool
|
|||
m_box_mode = box_mode;
|
||||
m_create_layers = create_other_layers;
|
||||
|
||||
set_cell_conflict_resolution (cc_resolution);
|
||||
|
||||
layout.start_changes ();
|
||||
do_read (layout);
|
||||
layout.end_changes ();
|
||||
try {
|
||||
do_read (layout);
|
||||
finish (layout);
|
||||
layout.end_changes ();
|
||||
} catch (...) {
|
||||
layout.end_changes ();
|
||||
throw;
|
||||
}
|
||||
|
||||
return m_layer_map;
|
||||
}
|
||||
|
|
@ -235,7 +243,6 @@ GDS2ReaderBase::do_read (db::Layout &layout)
|
|||
{
|
||||
m_cellname = "";
|
||||
m_libname = "";
|
||||
m_mapped_cellnames.clear ();
|
||||
|
||||
// read header
|
||||
if (get_record () != sHEADER) {
|
||||
|
|
@ -349,7 +356,7 @@ GDS2ReaderBase::do_read (db::Layout &layout)
|
|||
|
||||
} else {
|
||||
|
||||
db::cell_index_type cell_index = make_cell (layout, m_cellname.c_str (), false);
|
||||
db::cell_index_type cell_index = make_cell (layout, m_cellname);
|
||||
|
||||
db::Cell *cell = &layout.cell (cell_index);
|
||||
|
||||
|
|
@ -359,8 +366,6 @@ GDS2ReaderBase::do_read (db::Layout &layout)
|
|||
if (layout.recover_proxy_as (cell_index, ctx->second.begin (), ctx->second.end (), &layer_mapping)) {
|
||||
// ignore everything in that cell since it is created by the import:
|
||||
cell = 0;
|
||||
// marks the cell for begin addressed by REF's despite being a proxy:
|
||||
m_mapped_cellnames.insert (std::make_pair (m_cellname, m_cellname));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -973,54 +978,6 @@ GDS2ReaderBase::read_box (db::Layout &layout, db::Cell &cell)
|
|||
}
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
GDS2ReaderBase::make_cell (db::Layout &layout, const char *cn, bool for_instance)
|
||||
{
|
||||
db::cell_index_type ci = 0;
|
||||
|
||||
// map to the real name which maybe a different one due to localization
|
||||
// of proxy cells (they are not to be reopened)
|
||||
bool is_mapped = false;
|
||||
if (! m_mapped_cellnames.empty ()) {
|
||||
std::map<tl::string, tl::string>::const_iterator n = m_mapped_cellnames.find (cn);
|
||||
if (n != m_mapped_cellnames.end ()) {
|
||||
cn = n->second.c_str ();
|
||||
is_mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn);
|
||||
if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) {
|
||||
|
||||
// cell already there: just add instance (cell might have been created through forward reference)
|
||||
// NOTE: we don't address "reopened" proxies as proxies are always local to a layout
|
||||
|
||||
ci = c.second;
|
||||
|
||||
// mark the cell as read
|
||||
if (! for_instance) {
|
||||
layout.cell (ci).set_ghost_cell (false);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ci = layout.add_cell (cn);
|
||||
|
||||
if (for_instance) {
|
||||
// mark this cell a "ghost cell" until it's actually read
|
||||
layout.cell (ci).set_ghost_cell (true);
|
||||
}
|
||||
|
||||
if (c.first) {
|
||||
// this cell has been given a new name: remember this name for localization
|
||||
m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
void
|
||||
GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, tl::vector<db::CellInstArray> &instances, tl::vector<db::CellInstArrayWithProperties> &instances_with_props)
|
||||
{
|
||||
|
|
@ -1033,7 +990,7 @@ GDS2ReaderBase::read_ref (db::Layout &layout, db::Cell & /*cell*/, bool array, t
|
|||
error (tl::to_string (tr ("SNAME record expected")));
|
||||
}
|
||||
|
||||
db::cell_index_type ci = make_cell (layout, get_string (), true);
|
||||
db::cell_index_type ci = cell_for_instance (layout, get_string ());
|
||||
|
||||
bool mirror = false;
|
||||
int angle = 0;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "dbLayout.h"
|
||||
#include "dbReader.h"
|
||||
#include "dbStreamLayers.h"
|
||||
#include "dbCommonReader.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
|
|
@ -49,7 +50,7 @@ struct GDS2XY
|
|||
* @brief The GDS2 format basic stream reader
|
||||
*/
|
||||
class DB_PLUGIN_PUBLIC GDS2ReaderBase
|
||||
: public ReaderBase
|
||||
: public CommonReader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
|
|
@ -86,20 +87,21 @@ protected:
|
|||
* @param enable_properties A flag indicating whether to read user properties
|
||||
* @param allow_multi_xy_records If true, tries to check for multiple XY records for BOUNDARY elements
|
||||
* @param box_mode How to treat BOX records (0: ignore, 1: as rectangles, 2: as boundaries, 3: error)
|
||||
* @param cc_resolution The cell name conflict resolution mode
|
||||
* @return The LayerMap object that tells where which layer was loaded
|
||||
*/
|
||||
const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode);
|
||||
const LayerMap &basic_read (db::Layout &layout, const LayerMap &layer_map, bool create_other_layers, bool enable_text_objects, bool enable_properties, bool allow_multi_xy_records, unsigned int box_mode, db::CommonReader::CellConflictResolution cc_resolution);
|
||||
|
||||
/**
|
||||
* @brief Accessor method to the current cellname
|
||||
*/
|
||||
const tl::string &cellname () const { return m_cellname; }
|
||||
const std::string &cellname () const { return m_cellname; }
|
||||
|
||||
private:
|
||||
friend class GDS2ReaderLayerMapping;
|
||||
|
||||
LayerMap m_layer_map;
|
||||
tl::string m_cellname;
|
||||
std::string m_cellname;
|
||||
std::string m_libname;
|
||||
double m_dbu, m_dbuu;
|
||||
bool m_create_layers;
|
||||
|
|
@ -109,7 +111,6 @@ private:
|
|||
unsigned int m_box_mode;
|
||||
std::map <tl::string, std::vector<std::string> > m_context_info;
|
||||
std::vector <db::Point> m_all_points;
|
||||
std::map <tl::string, tl::string> m_mapped_cellnames;
|
||||
|
||||
void read_context_info_cell ();
|
||||
void read_boundary (db::Layout &layout, db::Cell &cell, bool from_box_record);
|
||||
|
|
@ -117,7 +118,6 @@ private:
|
|||
void read_text (db::Layout &layout, db::Cell &cell);
|
||||
void read_box (db::Layout &layout, db::Cell &cell);
|
||||
void read_ref (db::Layout &layout, db::Cell &cell, bool array, tl::vector<db::CellInstArray> &instances, tl::vector<db::CellInstArrayWithProperties> &insts_wp);
|
||||
db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance);
|
||||
|
||||
void do_read (db::Layout &layout);
|
||||
|
||||
|
|
@ -125,12 +125,15 @@ private:
|
|||
std::pair <bool, db::properties_id_type> finish_element (db::PropertiesRepository &rep);
|
||||
void finish_element ();
|
||||
|
||||
virtual void common_reader_error (const std::string &msg) { error (msg); }
|
||||
virtual void common_reader_warn (const std::string &msg) { warn (msg); }
|
||||
|
||||
virtual void error (const std::string &txt) = 0;
|
||||
virtual void warn (const std::string &txt) = 0;
|
||||
|
||||
virtual std::string path () const = 0;
|
||||
virtual const char *get_string () = 0;
|
||||
virtual void get_string (tl::string &s) const = 0;
|
||||
virtual void get_string (std::string &s) const = 0;
|
||||
virtual int get_int () = 0;
|
||||
virtual short get_short () = 0;
|
||||
virtual unsigned short get_ushort () = 0;
|
||||
|
|
|
|||
|
|
@ -366,6 +366,8 @@ TEST(Bug_121_1)
|
|||
reader.read (layout);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
std::string fn_au (tl::testsrc () + "/testdata/gds/bug_121_au1.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
|
@ -434,3 +436,100 @@ TEST(3_AdvancedMapping)
|
|||
std::string fn_au (tl::testsrc () + "/testdata/gds/alm_au.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
||||
TEST(4_CollectModeRename)
|
||||
{
|
||||
db::Manager m (false);
|
||||
db::Layout layout (&m);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::RenameCell;
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_rename_au.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
||||
TEST(4_CollectModeOverwrite)
|
||||
{
|
||||
db::Manager m (false);
|
||||
db::Layout layout (&m);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::OverwriteCell;
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_overwrite_au.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
||||
TEST(4_CollectModeSkip)
|
||||
{
|
||||
db::Manager m (false);
|
||||
db::Layout layout (&m);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::SkipNewCell;
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_skip_au.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
||||
TEST(4_CollectModeAdd)
|
||||
{
|
||||
db::Manager m (false);
|
||||
db::Layout layout (&m);
|
||||
|
||||
db::LoadLayoutOptions options;
|
||||
options.get_options<db::CommonReaderOptions> ().cell_conflict_resolution = db::CommonReader::AddToCell;
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_basic.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
{
|
||||
tl::InputStream file (tl::testsrc () + "/testdata/gds/collect_added.gds");
|
||||
db::Reader reader (file);
|
||||
reader.read (layout, options);
|
||||
}
|
||||
|
||||
std::string fn_au (tl::testsrc () + "/testdata/gds/collect_add_au.gds");
|
||||
db::compare_layouts (_this, layout, fn_au, db::WriteGDS2, 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,9 +144,12 @@ OASISReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
|
|||
m_read_all_properties = oasis_options.read_all_properties;
|
||||
m_expect_strict_mode = oasis_options.expect_strict_mode;
|
||||
|
||||
set_cell_conflict_resolution (common_options.cell_conflict_resolution);
|
||||
|
||||
layout.start_changes ();
|
||||
try {
|
||||
do_read (layout);
|
||||
finish (layout);
|
||||
layout.end_changes ();
|
||||
} catch (...) {
|
||||
layout.end_changes ();
|
||||
|
|
@ -758,17 +761,11 @@ OASISReader::do_read (db::Layout &layout)
|
|||
id_mode propstring_id_mode = any;
|
||||
id_mode propname_id_mode = any;
|
||||
|
||||
m_cellnames.clear ();
|
||||
m_cellname_properties.clear ();
|
||||
m_textstrings.clear ();
|
||||
m_propstrings.clear ();
|
||||
m_propnames.clear ();
|
||||
m_layernames.clear ();
|
||||
m_cells_by_id.clear ();
|
||||
m_cells_by_name.clear ();
|
||||
m_defined_cells_by_id.clear ();
|
||||
m_defined_cells_by_name.clear ();
|
||||
m_mapped_cellnames.clear ();
|
||||
|
||||
m_instances.clear ();
|
||||
m_instances_with_props.clear ();
|
||||
|
|
@ -826,9 +823,7 @@ OASISReader::do_read (db::Layout &layout)
|
|||
get (id);
|
||||
}
|
||||
|
||||
if (! m_cellnames.insert (std::make_pair (id, name)).second) {
|
||||
error (tl::sprintf (tl::to_string (tr ("A CELLNAME with id %ld is present already")), id));
|
||||
}
|
||||
rename_cell (layout, id, name);
|
||||
|
||||
reset_modal_variables ();
|
||||
|
||||
|
|
@ -1150,36 +1145,17 @@ OASISReader::do_read (db::Layout &layout)
|
|||
|
||||
unsigned long id = 0;
|
||||
get (id);
|
||||
if (! m_defined_cells_by_id.insert (id).second) {
|
||||
|
||||
std::pair<bool, db::cell_index_type> cc = cell_by_id (id);
|
||||
if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("A cell with id %ld is defined already")), id));
|
||||
}
|
||||
|
||||
std::map <unsigned long, db::cell_index_type>::const_iterator c = m_cells_by_id.find (id);
|
||||
if (c != m_cells_by_id.end ()) {
|
||||
|
||||
cell_index = c->second;
|
||||
layout.cell (cell_index).set_ghost_cell (false);
|
||||
|
||||
} else {
|
||||
|
||||
std::map <unsigned long, std::string>::const_iterator name = m_cellnames.find (id);
|
||||
if (name == m_cellnames.end ()) {
|
||||
|
||||
cell_index = layout.add_cell ();
|
||||
// force a cell rename to empty to avoid name clashes of the generated
|
||||
// $x names with the same inside the OASIS file.
|
||||
layout.rename_cell (cell_index, "");
|
||||
m_forward_references.insert (std::make_pair (id, cell_index));
|
||||
|
||||
} else {
|
||||
|
||||
cell_index = make_cell (layout, name->second.c_str (), false);
|
||||
m_cells_by_name.insert (std::make_pair (name->second, cell_index));
|
||||
|
||||
}
|
||||
|
||||
m_cells_by_id.insert (std::make_pair (id, cell_index));
|
||||
cell_index = make_cell (layout, id);
|
||||
|
||||
m_cellname = name_for_id (id);
|
||||
if (m_cellname.empty ()) {
|
||||
m_cellname = std::string ("#") + tl::to_string (id);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -1189,22 +1165,15 @@ OASISReader::do_read (db::Layout &layout)
|
|||
}
|
||||
|
||||
std::string name = get_str ();
|
||||
if (! m_defined_cells_by_name.insert (name).second) {
|
||||
|
||||
std::pair<bool, db::cell_index_type> cc = cell_by_name (name);
|
||||
if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("A cell with name %s is defined already")), name.c_str ()));
|
||||
}
|
||||
|
||||
std::map <std::string, db::cell_index_type>::const_iterator c = m_cells_by_name.find (name);
|
||||
if (c != m_cells_by_name.end ()) {
|
||||
cell_index = make_cell (layout, name);
|
||||
|
||||
cell_index = c->second;
|
||||
layout.cell (cell_index).set_ghost_cell (false);
|
||||
|
||||
} else {
|
||||
|
||||
cell_index = make_cell (layout, name.c_str (), false);
|
||||
m_cells_by_name.insert (std::make_pair (name, cell_index));
|
||||
|
||||
}
|
||||
m_cellname = name;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1326,72 +1295,16 @@ OASISReader::do_read (db::Layout &layout)
|
|||
|
||||
}
|
||||
|
||||
for (std::map <unsigned long, db::cell_index_type>::const_iterator fw = m_forward_references.begin (); fw != m_forward_references.end (); ++fw) {
|
||||
|
||||
std::map <unsigned long, std::string>::const_iterator cn = m_cellnames.find (fw->first);
|
||||
if (cn == m_cellnames.end ()) {
|
||||
|
||||
error (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), fw->first));
|
||||
|
||||
} else {
|
||||
|
||||
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn->second.c_str ());
|
||||
if (c.first) {
|
||||
|
||||
// needed, since we have disabled updates
|
||||
layout.force_update ();
|
||||
|
||||
// add-on reading of forward-referenced cell: need to copy the new cell to the original one plus
|
||||
// change instances of the new cell and delete the new cell then.
|
||||
|
||||
const db::Cell &new_cell = layout.cell (fw->second);
|
||||
db::Cell &org_cell = layout.cell (c.second);
|
||||
|
||||
// copy over the instances
|
||||
for (db::Cell::const_iterator i = new_cell.begin (); ! i.at_end (); ++i) {
|
||||
org_cell.insert (*i);
|
||||
}
|
||||
|
||||
// copy over the shapes
|
||||
for (unsigned int l = 0; l < layout.layers (); ++l) {
|
||||
if (layout.is_valid_layer (l) && ! new_cell.shapes (l).empty ()) {
|
||||
org_cell.shapes (l).insert (new_cell.shapes (l));
|
||||
}
|
||||
}
|
||||
|
||||
// replace all instances of the new cell with the original one
|
||||
std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
|
||||
for (db::Cell::parent_inst_iterator pi = new_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
|
||||
parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
|
||||
}
|
||||
|
||||
for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
|
||||
db::CellInstArray ia = p->second.cell_inst ();
|
||||
ia.object ().cell_index (org_cell.cell_index ());
|
||||
layout.cell (p->first).replace (p->second, ia);
|
||||
}
|
||||
|
||||
// finally delete the new cell
|
||||
layout.delete_cell (new_cell.cell_index ());
|
||||
|
||||
} else {
|
||||
layout.rename_cell (fw->second, cn->second.c_str ());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// attach the properties found in CELLNAME to the cells (which may have other properties)
|
||||
for (std::map<unsigned long, db::properties_id_type>::const_iterator p = m_cellname_properties.begin (); p != m_cellname_properties.end (); ++p) {
|
||||
|
||||
std::map <unsigned long, db::cell_index_type>::const_iterator c = m_cells_by_id.find (p->first);
|
||||
if (c != m_cells_by_id.end ()) {
|
||||
std::pair<bool, db::cell_index_type> c = cell_by_id (p->first);
|
||||
if (c.first) {
|
||||
|
||||
db::PropertiesRepository::properties_set cnp = layout.properties_repository ().properties (p->second);
|
||||
|
||||
// Merge existing properties with the ones from CELLNAME
|
||||
db::Cell &cell = layout.cell (c->second);
|
||||
db::Cell &cell = layout.cell (c.second);
|
||||
if (cell.prop_id () != 0) {
|
||||
db::PropertiesRepository::properties_set cp = layout.properties_repository ().properties (cell.prop_id ());
|
||||
cnp.insert (cp.begin (), cp.end ());
|
||||
|
|
@ -1844,54 +1757,6 @@ OASISReader::read_repetition ()
|
|||
return mm_repetition.get ().size () > 1;
|
||||
}
|
||||
|
||||
db::cell_index_type
|
||||
OASISReader::make_cell (db::Layout &layout, const char *cn, bool for_instance)
|
||||
{
|
||||
db::cell_index_type ci = 0;
|
||||
|
||||
// map to the real name which maybe a different one due to localization
|
||||
// of proxy cells (they are not to be reopened)
|
||||
bool is_mapped = false;
|
||||
if (! m_mapped_cellnames.empty ()) {
|
||||
std::map<tl::string, tl::string>::const_iterator n = m_mapped_cellnames.find (cn);
|
||||
if (n != m_mapped_cellnames.end ()) {
|
||||
cn = n->second.c_str ();
|
||||
is_mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn);
|
||||
if (c.first && (is_mapped || ! layout.cell (c.second).is_proxy ())) {
|
||||
|
||||
// cell already there: just add instance (cell might have been created through forward reference)
|
||||
// NOTE: we don't address "reopened" proxies as proxies are always local to a layout
|
||||
|
||||
ci = c.second;
|
||||
|
||||
// mark the cell as read
|
||||
if (! for_instance) {
|
||||
layout.cell (ci).set_ghost_cell (false);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
ci = layout.add_cell (cn);
|
||||
|
||||
if (for_instance) {
|
||||
// mark this cell a "ghost cell" until it's actually read
|
||||
layout.cell (ci).set_ghost_cell (true);
|
||||
}
|
||||
|
||||
if (c.first) {
|
||||
// this cell has been given a new name: remember this name for localization
|
||||
m_mapped_cellnames.insert (std::make_pair (cn, layout.cell_name (ci)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
void
|
||||
OASISReader::do_read_placement (unsigned char r,
|
||||
bool xy_absolute,
|
||||
|
|
@ -1909,31 +1774,8 @@ OASISReader::do_read_placement (unsigned char r,
|
|||
// cell by id
|
||||
unsigned long id;
|
||||
get (id);
|
||||
std::map <unsigned long, db::cell_index_type>::const_iterator cid = m_cells_by_id.find (id);
|
||||
if (cid == m_cells_by_id.end ()) {
|
||||
|
||||
// create the cell
|
||||
std::map <unsigned long, std::string>::const_iterator name = m_cellnames.find (id);
|
||||
if (name == m_cellnames.end ()) {
|
||||
|
||||
mm_placement_cell = layout.add_cell ();
|
||||
m_forward_references.insert (std::make_pair (id, mm_placement_cell.get ()));
|
||||
|
||||
// temporarily mark as "ghost cell"
|
||||
layout.cell (mm_placement_cell.get ()).set_ghost_cell (true);
|
||||
|
||||
} else {
|
||||
|
||||
mm_placement_cell = make_cell (layout, name->second.c_str (), true);
|
||||
m_cells_by_name.insert (std::make_pair (name->second, mm_placement_cell.get ()));
|
||||
|
||||
}
|
||||
|
||||
m_cells_by_id.insert (std::make_pair (id, mm_placement_cell.get ()));
|
||||
|
||||
} else {
|
||||
mm_placement_cell = cid->second;
|
||||
}
|
||||
mm_placement_cell = cell_for_instance (layout, id);
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -1941,15 +1783,7 @@ OASISReader::do_read_placement (unsigned char r,
|
|||
std::string name;
|
||||
get_str (name);
|
||||
|
||||
std::map <std::string, db::cell_index_type>::const_iterator cid = m_cells_by_name.find (name);
|
||||
if (cid == m_cells_by_name.end ()) {
|
||||
|
||||
mm_placement_cell = make_cell (layout, name.c_str (), true);
|
||||
m_cells_by_name.insert (std::make_pair (name, mm_placement_cell.get ()));
|
||||
|
||||
} else {
|
||||
mm_placement_cell = cid->second;
|
||||
}
|
||||
mm_placement_cell = cell_for_instance (layout, name);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3433,7 +3267,6 @@ OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout)
|
|||
m_instances_with_props.clear ();
|
||||
|
||||
m_progress.set (m_stream.pos ());
|
||||
m_cellname = layout.cell_name (cell_index);
|
||||
|
||||
bool xy_absolute = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "dbOASISFormat.h"
|
||||
#include "dbStreamLayers.h"
|
||||
#include "dbPropertiesRepository.h"
|
||||
#include "dbCommonReader.h"
|
||||
|
||||
#include "tlException.h"
|
||||
#include "tlInternational.h"
|
||||
|
|
@ -62,7 +63,7 @@ public:
|
|||
* @brief The OASIS format stream reader
|
||||
*/
|
||||
class DB_PLUGIN_PUBLIC OASISReader
|
||||
: public ReaderBase,
|
||||
: public CommonReader,
|
||||
public OASISDiagnostics
|
||||
{
|
||||
public:
|
||||
|
|
@ -118,6 +119,7 @@ public:
|
|||
*/
|
||||
virtual const char *format () const { return "OASIS"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Issue an error with positional information
|
||||
*
|
||||
|
|
@ -132,6 +134,9 @@ public:
|
|||
*/
|
||||
virtual void warn (const std::string &txt);
|
||||
|
||||
virtual void common_reader_error (const std::string &msg) { error (msg); }
|
||||
virtual void common_reader_warn (const std::string &msg) { warn (msg); }
|
||||
|
||||
private:
|
||||
friend class OASISReaderLayerMapping;
|
||||
|
||||
|
|
@ -194,7 +199,6 @@ private:
|
|||
modal_variable<bool> mm_last_property_is_sprop;
|
||||
modal_variable<property_value_list> mm_last_value_list;
|
||||
|
||||
std::map <unsigned long, std::string> m_cellnames;
|
||||
std::map <unsigned long, db::properties_id_type> m_cellname_properties;
|
||||
std::map <unsigned long, std::string> m_textstrings;
|
||||
std::map <unsigned long, const db::StringRef *> m_text_forward_references;
|
||||
|
|
@ -205,18 +209,11 @@ private:
|
|||
tl::vector<db::CellInstArray> m_instances;
|
||||
tl::vector<db::CellInstArrayWithProperties> m_instances_with_props;
|
||||
|
||||
std::map <unsigned long, db::cell_index_type> m_cells_by_id;
|
||||
std::map <unsigned long, db::cell_index_type> m_forward_references;
|
||||
std::map <std::string, db::cell_index_type> m_cells_by_name;
|
||||
bool m_create_layers;
|
||||
bool m_read_texts;
|
||||
bool m_read_properties;
|
||||
bool m_read_all_properties;
|
||||
|
||||
std::set <unsigned long> m_defined_cells_by_id;
|
||||
std::set <std::string> m_defined_cells_by_name;
|
||||
std::map <tl::string, tl::string> m_mapped_cellnames;
|
||||
|
||||
std::map <unsigned long, db::property_names_id_type> m_propname_forward_references;
|
||||
std::map <unsigned long, std::string> m_propvalue_forward_references;
|
||||
db::property_names_id_type m_s_gds_property_name_id;
|
||||
|
|
@ -238,7 +235,6 @@ private:
|
|||
void do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
|
||||
void do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
|
||||
void do_read_circle (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout);
|
||||
db::cell_index_type make_cell (db::Layout &layout, const char *cn, bool for_instance);
|
||||
|
||||
void reset_modal_variables ();
|
||||
|
||||
|
|
|
|||
|
|
@ -1150,6 +1150,18 @@ OASISWriter::reset_modal_variables ()
|
|||
mm_last_value_list.reset ();
|
||||
}
|
||||
|
||||
static bool must_write_cell (const db::Cell &cref)
|
||||
{
|
||||
// Don't write proxy cells which are not employed
|
||||
return ! cref.is_proxy () || ! cref.is_top ();
|
||||
}
|
||||
|
||||
static bool skip_cell_body (const db::Cell &cref)
|
||||
{
|
||||
// Skip cell bodies for ghost cells unless empty (they are not longer ghost cells in this case)
|
||||
return cref.is_ghost_cell () && cref.empty ();
|
||||
}
|
||||
|
||||
void
|
||||
OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
|
||||
{
|
||||
|
|
@ -1184,13 +1196,13 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
|
|||
cells_by_index.reserve (cell_set.size ());
|
||||
|
||||
for (db::Layout::bottom_up_const_iterator cell = layout.begin_bottom_up (); cell != layout.end_bottom_up (); ++cell) {
|
||||
if (cell_set.find (*cell) != cell_set.end ()) {
|
||||
if (cell_set.find (*cell) != cell_set.end () && must_write_cell (layout.cell (*cell))) {
|
||||
cells.push_back (*cell);
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Layout::const_iterator cell = layout.begin (); cell != layout.end (); ++cell) {
|
||||
if (cell_set.find (cell->cell_index ()) != cell_set.end ()) {
|
||||
if (cell_set.find (cell->cell_index ()) != cell_set.end () && must_write_cell (layout.cell (cell->cell_index ()))) {
|
||||
cells_by_index.push_back (cell->cell_index ());
|
||||
}
|
||||
}
|
||||
|
|
@ -1594,80 +1606,79 @@ OASISWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::Save
|
|||
const db::Cell &cref (layout.cell (*cell));
|
||||
mp_cell = &cref;
|
||||
|
||||
// don't write ghost cells unless they are not empty (any more)
|
||||
// also don't write proxy cells which are not employed
|
||||
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
|
||||
// skip cell body if the cell is not to be written
|
||||
if (skip_cell_body (cref)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// cell header
|
||||
// cell header
|
||||
|
||||
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
|
||||
cell_positions.insert (std::make_pair (*cell, mp_stream->pos ()));
|
||||
|
||||
write_record_id (13); // CELL
|
||||
write ((unsigned long) *cell);
|
||||
write_record_id (13); // CELL
|
||||
write ((unsigned long) *cell);
|
||||
|
||||
reset_modal_variables ();
|
||||
reset_modal_variables ();
|
||||
|
||||
if (m_options.write_cblocks) {
|
||||
begin_cblock ();
|
||||
}
|
||||
if (m_options.write_cblocks) {
|
||||
begin_cblock ();
|
||||
}
|
||||
|
||||
// context information as property named KLAYOUT_CONTEXT
|
||||
if (cref.is_proxy () && options.write_context_info ()) {
|
||||
// context information as property named KLAYOUT_CONTEXT
|
||||
if (cref.is_proxy () && options.write_context_info ()) {
|
||||
|
||||
context_prop_strings.clear ();
|
||||
context_prop_strings.clear ();
|
||||
|
||||
if (layout.get_context_info (*cell, context_prop_strings)) {
|
||||
if (layout.get_context_info (*cell, context_prop_strings)) {
|
||||
|
||||
write_record_id (28);
|
||||
write_byte (char (0xf6));
|
||||
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
|
||||
tl_assert (pni != m_propnames.end ());
|
||||
write (pni->second);
|
||||
write_record_id (28);
|
||||
write_byte (char (0xf6));
|
||||
std::map <std::string, unsigned long>::const_iterator pni = m_propnames.find (klayout_context_name);
|
||||
tl_assert (pni != m_propnames.end ());
|
||||
write (pni->second);
|
||||
|
||||
write ((unsigned long) context_prop_strings.size ());
|
||||
|
||||
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
|
||||
write_byte (14); // b-string by reference number
|
||||
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
|
||||
tl_assert (psi != m_propstrings.end ());
|
||||
write (psi->second);
|
||||
}
|
||||
|
||||
mm_last_property_name = klayout_context_name;
|
||||
mm_last_property_is_sprop = false;
|
||||
mm_last_value_list.reset ();
|
||||
write ((unsigned long) context_prop_strings.size ());
|
||||
|
||||
for (std::vector <std::string>::const_iterator c = context_prop_strings.begin (); c != context_prop_strings.end (); ++c) {
|
||||
write_byte (14); // b-string by reference number
|
||||
std::map <std::string, unsigned long>::const_iterator psi = m_propstrings.find (*c);
|
||||
tl_assert (psi != m_propstrings.end ());
|
||||
write (psi->second);
|
||||
}
|
||||
|
||||
mm_last_property_name = klayout_context_name;
|
||||
mm_last_property_is_sprop = false;
|
||||
mm_last_value_list.reset ();
|
||||
|
||||
}
|
||||
|
||||
if (cref.prop_id () != 0) {
|
||||
write_props (cref.prop_id ());
|
||||
}
|
||||
|
||||
// instances
|
||||
if (cref.cell_instances () > 0) {
|
||||
write_insts (cell_set);
|
||||
}
|
||||
|
||||
// shapes
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
const db::Shapes &shapes = cref.shapes (l->first);
|
||||
if (! shapes.empty ()) {
|
||||
write_shapes (l->second, shapes);
|
||||
m_progress.set (mp_stream->pos ());
|
||||
}
|
||||
}
|
||||
|
||||
// end CBLOCK if required
|
||||
if (m_options.write_cblocks) {
|
||||
end_cblock ();
|
||||
}
|
||||
|
||||
// end of cell
|
||||
|
||||
}
|
||||
|
||||
if (cref.prop_id () != 0) {
|
||||
write_props (cref.prop_id ());
|
||||
}
|
||||
|
||||
// instances
|
||||
if (cref.cell_instances () > 0) {
|
||||
write_insts (cell_set);
|
||||
}
|
||||
|
||||
// shapes
|
||||
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
|
||||
const db::Shapes &shapes = cref.shapes (l->first);
|
||||
if (! shapes.empty ()) {
|
||||
write_shapes (l->second, shapes);
|
||||
m_progress.set (mp_stream->pos ());
|
||||
}
|
||||
}
|
||||
|
||||
// end CBLOCK if required
|
||||
if (m_options.write_cblocks) {
|
||||
end_cblock ();
|
||||
}
|
||||
|
||||
// end of cell
|
||||
|
||||
}
|
||||
|
||||
// write cell table at the end in strict mode (in that mode we need the cell positions
|
||||
|
|
|
|||
|
|
@ -570,11 +570,11 @@ TEST(100)
|
|||
|
||||
const char *expected =
|
||||
"begin_lib 0.001\n"
|
||||
"begin_cell {$4}\n"
|
||||
"end_cell\n"
|
||||
"begin_cell {$1}\n"
|
||||
"box 1 0 {0 100} {1000 1200}\n"
|
||||
"end_cell\n"
|
||||
"begin_cell {$4}\n"
|
||||
"end_cell\n"
|
||||
"begin_cell {$3}\n"
|
||||
"sref {$1} 90 1 1 {-10 20}\n"
|
||||
"sref {$4} 90 1 1 {-10 20}\n"
|
||||
|
|
@ -1327,14 +1327,14 @@ TEST(116)
|
|||
" {{name} {117}}\n"
|
||||
"}\n"
|
||||
"begin_libp $props 0.001\n"
|
||||
"begin_cell {$2}\n"
|
||||
"end_cell\n"
|
||||
"set props {\n"
|
||||
" {42 {42}}\n"
|
||||
"}\n"
|
||||
"begin_cellp $props {$1}\n"
|
||||
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
|
||||
"end_cell\n"
|
||||
"begin_cell {$2}\n"
|
||||
"end_cell\n"
|
||||
"end_lib\n"
|
||||
;
|
||||
|
||||
|
|
@ -1376,14 +1376,14 @@ TEST(116)
|
|||
" {{name} {117}}\n"
|
||||
"}\n"
|
||||
"begin_libp $props 0.001\n"
|
||||
"begin_cell {$2}\n"
|
||||
"end_cell\n"
|
||||
"set props {\n"
|
||||
" {42 {42}}\n"
|
||||
"}\n"
|
||||
"begin_cellp $props {$1}\n"
|
||||
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
|
||||
"end_cell\n"
|
||||
"begin_cell {$2}\n"
|
||||
"end_cell\n"
|
||||
"end_lib\n"
|
||||
;
|
||||
|
||||
|
|
@ -1431,17 +1431,17 @@ TEST(116)
|
|||
"}\n"
|
||||
"begin_libp $props 0.001\n"
|
||||
"set props {\n"
|
||||
" {{S_BOUNDING_BOX} {2,0,0,0,0}}\n"
|
||||
"}\n"
|
||||
"begin_cellp $props {$2}\n"
|
||||
"end_cell\n"
|
||||
"set props {\n"
|
||||
" {{S_BOUNDING_BOX} {0,0,100,1000,1100}}\n"
|
||||
" {42 {42}}\n"
|
||||
"}\n"
|
||||
"begin_cellp $props {$1}\n"
|
||||
"path 1 0 0 0 0 {0 100} {1000 1200}\n"
|
||||
"end_cell\n"
|
||||
"set props {\n"
|
||||
" {{S_BOUNDING_BOX} {2,0,0,0,0}}\n"
|
||||
"}\n"
|
||||
"begin_cellp $props {$2}\n"
|
||||
"end_cell\n"
|
||||
"end_lib\n"
|
||||
;
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -7,10 +7,10 @@
|
|||
# <test-intention>Explicit assignment of ID's</test-intention>
|
||||
# <content>
|
||||
# begin_lib 0.001
|
||||
# begin_cell {ABC}
|
||||
# end_cell
|
||||
# begin_cell {XYZ}
|
||||
# end_cell
|
||||
# begin_cell {ABC}
|
||||
# end_cell
|
||||
# end_lib
|
||||
# </content>
|
||||
# </test>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
begin_lib 0.001
|
||||
begin_cell {ABC}
|
||||
end_cell
|
||||
begin_cell {XYZ}
|
||||
end_cell
|
||||
begin_cell {ABC}
|
||||
end_cell
|
||||
end_lib
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
begin_lib 0.001
|
||||
begin_cell {ABC}
|
||||
end_cell
|
||||
begin_cell { XYZ}
|
||||
end_cell
|
||||
begin_cell {ABC}
|
||||
end_cell
|
||||
end_lib
|
||||
|
|
|
|||
|
|
@ -53,6 +53,14 @@ class DBReaders_TestClass < TestBase
|
|||
opt.properties_enabled = false
|
||||
assert_equal(opt.properties_enabled?, false)
|
||||
|
||||
assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::AddToCell)
|
||||
opt.cell_conflict_resolution = RBA::LoadLayoutOptions::OverwriteCell
|
||||
assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::OverwriteCell)
|
||||
opt.cell_conflict_resolution = RBA::LoadLayoutOptions::SkipNewCell
|
||||
assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::SkipNewCell)
|
||||
opt.cell_conflict_resolution = RBA::LoadLayoutOptions::RenameCell
|
||||
assert_equal(opt.cell_conflict_resolution, RBA::LoadLayoutOptions::RenameCell)
|
||||
|
||||
end
|
||||
|
||||
# GDS2 Options
|
||||
|
|
|
|||
Loading…
Reference in New Issue