mirror of https://github.com/KLayout/klayout.git
Merge branch 'master' into issue-1271
This commit is contained in:
commit
1b13d8b591
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
@Library("osconfig") _
|
||||
|
||||
properties([disableConcurrentBuilds()])
|
||||
|
||||
// from shared library
|
||||
target = osconfig()
|
||||
docker_args = docker_args()
|
||||
|
||||
currentBuild.description = "Pipelined " + target
|
||||
|
||||
node("linux") {
|
||||
|
||||
artefacts = pwd() + "/artefacts"
|
||||
target_dir = artefacts + "/" + target
|
||||
|
||||
stage("Checkout sources") {
|
||||
|
||||
checkout scm
|
||||
checkout_private()
|
||||
|
||||
}
|
||||
|
||||
stage("Building target ${target}") {
|
||||
|
||||
withDockerContainer(image: "jenkins-${target}", args: docker_args) {
|
||||
// from shared library
|
||||
build(target, target_dir)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stage("Publish and test") {
|
||||
|
||||
parallel(
|
||||
"Publish": {
|
||||
|
||||
// from shared library - only publish for normal branch, not for PR
|
||||
if (! BRANCH_NAME.startsWith('PR')) {
|
||||
publish(BRANCH_NAME, target, target_dir)
|
||||
}
|
||||
|
||||
},
|
||||
"Unit testing": {
|
||||
|
||||
ut_result = "no-result"
|
||||
withDockerContainer(image: "jenkins-${target}", args: docker_args) {
|
||||
ut_result = run_ut(target)
|
||||
}
|
||||
|
||||
junit(testResults: ut_result)
|
||||
|
||||
},
|
||||
"Installtest": {
|
||||
|
||||
withDockerContainer(image: "jenkins-${target}-basic", args: docker_args) {
|
||||
// from shared library
|
||||
installtest_nopython(target, target_dir)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
properties([disableConcurrentBuilds()])
|
||||
|
||||
node("master") {
|
||||
node("linux") {
|
||||
|
||||
stage("Checkout sources") {
|
||||
checkout scm
|
||||
}
|
||||
|
||||
stage("Producing doc") {
|
||||
sh "./scripts/extract_user_doc.sh -i " + BRANCH_NAME
|
||||
linux_sh "./scripts/extract_user_doc.sh -i " + BRANCH_NAME
|
||||
}
|
||||
|
||||
stage("Publish doc") {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
properties([disableConcurrentBuilds()])
|
||||
|
||||
node("master") {
|
||||
node("linux") {
|
||||
|
||||
stage("Checkout sources") {
|
||||
checkout scm
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ currentBuild.description = "PyPI deployment " + platform
|
|||
docker_image = "jenkins-manylinux2014_x86_64-pypi"
|
||||
target = platform + ".whl"
|
||||
|
||||
node("master") {
|
||||
node("linux") {
|
||||
|
||||
stage("Checkout sources") {
|
||||
checkout scm
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>622</width>
|
||||
<width>669</width>
|
||||
<height>621</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
@ -1142,10 +1142,34 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>fmt_le</tabstop>
|
||||
<tabstop>main_position</tabstop>
|
||||
<tabstop>main_xalign</tabstop>
|
||||
<tabstop>main_yalign</tabstop>
|
||||
<tabstop>fmt_x_le</tabstop>
|
||||
<tabstop>xlabel_xalign</tabstop>
|
||||
<tabstop>xlabel_yalign</tabstop>
|
||||
<tabstop>fmt_y_le</tabstop>
|
||||
<tabstop>ylabel_xalign</tabstop>
|
||||
<tabstop>ylabel_yalign</tabstop>
|
||||
<tabstop>style_cb</tabstop>
|
||||
<tabstop>outline_cb</tabstop>
|
||||
<tabstop>segments_tab</tabstop>
|
||||
<tabstop>x0</tabstop>
|
||||
<tabstop>y0</tabstop>
|
||||
<tabstop>x1</tabstop>
|
||||
<tabstop>y1</tabstop>
|
||||
<tabstop>x2</tabstop>
|
||||
<tabstop>y2</tabstop>
|
||||
<tabstop>swap_points</tabstop>
|
||||
<tabstop>p1_to_layout</tabstop>
|
||||
<tabstop>p2_to_layout</tabstop>
|
||||
<tabstop>both_to_layout</tabstop>
|
||||
<tabstop>dx</tabstop>
|
||||
<tabstop>dy</tabstop>
|
||||
<tabstop>dd</tabstop>
|
||||
<tabstop>point_list</tabstop>
|
||||
<tabstop>points_edit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ Object::seg_p2 (size_t seg_index, const db::DPoint &p)
|
|||
void
|
||||
Object::p1 (const db::DPoint &p)
|
||||
{
|
||||
if (! p1 ().equal (p)) {
|
||||
if (m_points.size () < 1 || ! p1 ().equal (p)) {
|
||||
if (m_points.size () < 1) {
|
||||
m_points.push_back (p);
|
||||
} else {
|
||||
|
|
@ -348,7 +348,7 @@ Object::p1 (const db::DPoint &p)
|
|||
void
|
||||
Object::p2 (const db::DPoint &p)
|
||||
{
|
||||
if (! p2 ().equal (p)) {
|
||||
if (m_points.size () < 2 || ! p2 ().equal (p)) {
|
||||
if (m_points.size () < 2) {
|
||||
if (m_points.empty ()) {
|
||||
m_points.push_back (db::DPoint ());
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include "antObject.h"
|
||||
#include "antTemplate.h"
|
||||
|
||||
// NOTE: most tests are in ruby/antTest.rb
|
||||
|
||||
TEST(1)
|
||||
{
|
||||
ant::Template tmp = ant::Template ("title", "fmt_x", "fmt_y", "fmt",
|
||||
|
|
@ -48,3 +50,84 @@ TEST(1)
|
|||
EXPECT_EQ (a.category (), "cat");
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
{
|
||||
ant::Template tmp = ant::Template ("title", "fmt_x", "fmt_y", "fmt",
|
||||
ant::Object::STY_arrow_both,
|
||||
ant::Object::OL_diag_xy,
|
||||
true,
|
||||
lay::AC_Ortho,
|
||||
"cat");
|
||||
|
||||
ant::Object obj;
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "0,0");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "0,0");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 0);
|
||||
|
||||
obj.p1 (db::DPoint (1, 2));
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "1,2");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "1,2");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 1);
|
||||
|
||||
obj.p2 (db::DPoint (2, 3));
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "1,2");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "2,3");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 2);
|
||||
|
||||
obj = ant::Object ();
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "0,0");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "0,0");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 0);
|
||||
|
||||
obj.p1 (db::DPoint ());
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "0,0");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "0,0");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 1);
|
||||
|
||||
obj.p2 (db::DPoint ());
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "0,0");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "0,0");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 1);
|
||||
|
||||
obj = ant::Object (db::DPoint (1, 2), db::DPoint (2, 3), 0, tmp);
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "1,2");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "2,3");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 2);
|
||||
|
||||
obj = ant::Object (db::DPoint (1, 2), db::DPoint (1, 2), 0, tmp);
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "1,2");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "1,2");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 1);
|
||||
|
||||
obj = ant::Object (db::DPoint (), db::DPoint (), 0, tmp);
|
||||
|
||||
EXPECT_EQ (obj.p1 ().to_string (), "0,0");
|
||||
EXPECT_EQ (obj.p2 ().to_string (), "0,0");
|
||||
|
||||
EXPECT_EQ (int (obj.segments ()), 1);
|
||||
EXPECT_EQ (int (obj.points ().size ()), 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2143,6 +2143,10 @@ ms_extraction (db::EdgeProcessor &ep, bool resolve_holes)
|
|||
static db::Polygon
|
||||
do_minkowski_sum (const db::Polygon &a, const db::Edge &b, bool resolve_holes)
|
||||
{
|
||||
if (a.begin_hull () == a.end_hull ()) {
|
||||
return db::Polygon ();
|
||||
}
|
||||
|
||||
db::EdgeProcessor ep;
|
||||
db::ms_production (a, b.p1 (), b.p2 (), ep);
|
||||
return db::ms_extraction (ep, resolve_holes);
|
||||
|
|
@ -2161,7 +2165,9 @@ minkowski_sum (const db::Polygon &a, const db::Edge &b, bool rh)
|
|||
static db::Polygon
|
||||
do_minkowski_sum (const db::Polygon &a, const db::Polygon &b, bool resolve_holes)
|
||||
{
|
||||
tl_assert (a.begin_hull () != a.end_hull ());
|
||||
if (a.begin_hull () == a.end_hull () || b.begin_hull () == b.end_hull ()) {
|
||||
return db::Polygon ();
|
||||
}
|
||||
|
||||
db::Vector p0 = *a.begin_hull () - db::Point ();
|
||||
|
||||
|
|
|
|||
|
|
@ -1552,15 +1552,19 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
|
|||
m_alt_ac = ac_from_buttons (buttons);
|
||||
|
||||
// drag the vertex or edge/segment
|
||||
if (m_selection.size () == 1 && ! m_selection.begin ()->first.is_cell_inst () && (m_selection.begin ()->second.size () == 1 /*p*/ || m_selection.begin ()->second.size () == 3 /*p1,p2,edge*/)) {
|
||||
if (is_single_point_selection () || is_single_edge_selection ()) {
|
||||
|
||||
lay::PointSnapToObjectResult snap_details;
|
||||
|
||||
// for a single selected point or edge, m_start is the original position and we snap the target -
|
||||
// thus, we can bring the point on grid or to an object's edge or vertex
|
||||
snap_details = snap2 (p);
|
||||
m_current = snap_details.snapped_point;
|
||||
mouse_cursor_from_snap_details (snap_details);
|
||||
if (snap_details.object_snap == lay::PointSnapToObjectResult::NoObject) {
|
||||
m_current = m_start + snap (p - m_start);
|
||||
} else {
|
||||
m_current = snap_details.snapped_point;
|
||||
mouse_cursor_from_snap_details (snap_details);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -1601,6 +1605,17 @@ PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, boo
|
|||
return false;
|
||||
}
|
||||
|
||||
static db::DPoint
|
||||
projected_to_edge (const db::DEdge &edge, const db::DPoint &p)
|
||||
{
|
||||
if (edge.is_degenerate ()) {
|
||||
return edge.p1 ();
|
||||
} else {
|
||||
db::DVector v = edge.d () * (1.0 / edge.length ());
|
||||
return edge.p1 () + v * db::sprod (p - edge.p1 (), v);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio)
|
||||
{
|
||||
|
|
@ -1674,8 +1689,12 @@ PartialService::mouse_press_event (const db::DPoint &p, unsigned int buttons, bo
|
|||
|
||||
if (is_single_point_selection ()) {
|
||||
// for a single selected point we use the original point as the start location which
|
||||
// allows bringing a to grid
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = single_selected_point ();
|
||||
} else if (is_single_edge_selection ()) {
|
||||
// for an edge selection use the point projected to edge as the start location which
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = projected_to_edge (single_selected_edge (), p);
|
||||
} else {
|
||||
m_current = m_start = p;
|
||||
}
|
||||
|
|
@ -1863,8 +1882,12 @@ PartialService::mouse_click_event (const db::DPoint &p, unsigned int buttons, bo
|
|||
|
||||
if (is_single_point_selection ()) {
|
||||
// for a single selected point we use the original point as the start location which
|
||||
// allows bringing a to grid
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = single_selected_point ();
|
||||
} else if (is_single_edge_selection ()) {
|
||||
// for an edge selection use the point projected to edge as the start location which
|
||||
// allows bringing it to grid
|
||||
m_current = m_start = projected_to_edge (single_selected_edge (), p);
|
||||
} else {
|
||||
m_current = m_start = p;
|
||||
}
|
||||
|
|
@ -2390,6 +2413,28 @@ PartialService::is_single_point_selection () const
|
|||
return (m_selection.size () == 1 && ! m_selection.begin ()->first.is_cell_inst () && m_selection.begin ()->second.size () == 1 /*p*/);
|
||||
}
|
||||
|
||||
db::DEdge
|
||||
PartialService::single_selected_edge () const
|
||||
{
|
||||
// build the transformation variants cache and
|
||||
// use only the first one of the explicit transformations
|
||||
// TODO: clarify how this can be implemented in a more generic form or leave it thus.
|
||||
TransformationVariants tv (view ());
|
||||
const std::vector<db::DCplxTrans> *tv_list = tv.per_cv_and_layer (m_selection.begin ()->first.cv_index (), m_selection.begin ()->first.layer ());
|
||||
|
||||
const lay::CellView &cv = view ()->cellview (m_selection.begin ()->first.cv_index ());
|
||||
db::ICplxTrans gt (cv.context_trans () * m_selection.begin ()->first.trans ());
|
||||
db::CplxTrans tt = (*tv_list)[0] * db::CplxTrans (cv->layout ().dbu ()) * gt;
|
||||
|
||||
return tt * *m_selection.begin ()->second.begin ();
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::is_single_edge_selection () const
|
||||
{
|
||||
return (m_selection.size () == 1 && ! m_selection.begin ()->first.is_cell_inst () && m_selection.begin ()->second.size () == 3 /*p1,p2,edge*/);
|
||||
}
|
||||
|
||||
bool
|
||||
PartialService::select (const db::DBox &box, SelectionMode mode)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -363,7 +363,9 @@ private:
|
|||
void resize_markers (size_t n, bool transient);
|
||||
void resize_inst_markers (size_t n, bool transient);
|
||||
bool is_single_point_selection () const;
|
||||
bool is_single_edge_selection () const;
|
||||
db::DPoint single_selected_point () const;
|
||||
db::DEdge single_selected_edge () const;
|
||||
bool handle_guiding_shape_changes ();
|
||||
void transform_selection (const db::DTrans &move_trans);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1189,6 +1189,16 @@ Class<lay::LayerPropertiesNode> decl_LayerPropertiesNode (
|
|||
"Unlike the name suggests, this node will still contain a hierarchy of nodes below if the original "
|
||||
"node did so."
|
||||
) +
|
||||
method ("is_expanded?", &lay::LayerPropertiesNode::expanded,
|
||||
"@brief Gets a value indicating whether the layer tree node is expanded.\n"
|
||||
"This predicate has been introduced in version 0.28.6."
|
||||
) +
|
||||
method ("expanded=", &lay::LayerPropertiesNode::set_expanded, gsi::arg ("ex"),
|
||||
"@brief Set a value indicating whether the layer tree node is expanded.\n"
|
||||
"Setting this value to 'true' will expand (open) the tree node. Setting it to 'false' will collapse the node.\n"
|
||||
"\n"
|
||||
"This predicate has been introduced in version 0.28.6."
|
||||
) +
|
||||
method_ext ("add_child", &add_child0,
|
||||
"@brief Add a child entry\n"
|
||||
"@return A reference to the node created\n"
|
||||
|
|
|
|||
|
|
@ -591,6 +591,12 @@ LayerProperties::need_realize (unsigned int flags, bool /*force*/)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerProperties::expanded_state_changed ()
|
||||
{
|
||||
// .. no effect ..
|
||||
}
|
||||
|
||||
void
|
||||
LayerProperties::do_realize (const LayoutViewBase *view) const
|
||||
{
|
||||
|
|
@ -651,7 +657,7 @@ static unsigned int s_unique_id = 0;
|
|||
|
||||
LayerPropertiesNode::LayerPropertiesNode ()
|
||||
: LayerProperties (),
|
||||
m_list_index (0)
|
||||
m_list_index (0), m_expanded (false)
|
||||
{
|
||||
m_id = ++s_unique_id;
|
||||
}
|
||||
|
|
@ -663,7 +669,7 @@ LayerPropertiesNode::~LayerPropertiesNode ()
|
|||
|
||||
LayerPropertiesNode::LayerPropertiesNode (const LayerProperties &d)
|
||||
: LayerProperties (d),
|
||||
m_list_index (0)
|
||||
m_list_index (0), m_expanded (false)
|
||||
{
|
||||
m_id = ++s_unique_id;
|
||||
}
|
||||
|
|
@ -671,6 +677,7 @@ LayerPropertiesNode::LayerPropertiesNode (const LayerProperties &d)
|
|||
LayerPropertiesNode::LayerPropertiesNode (const LayerPropertiesNode &d)
|
||||
: LayerProperties (d), tl::Object (),
|
||||
m_list_index (0),
|
||||
m_expanded (d.m_expanded),
|
||||
m_children (d.m_children),
|
||||
m_id (d.m_id)
|
||||
{
|
||||
|
|
@ -687,6 +694,7 @@ LayerPropertiesNode::operator= (const LayerPropertiesNode &d)
|
|||
LayerProperties::operator= (d);
|
||||
|
||||
m_children = d.m_children;
|
||||
m_expanded = d.m_expanded;
|
||||
m_id = d.m_id;
|
||||
|
||||
for (iterator c = m_children.begin (); c != m_children.end (); ++c) {
|
||||
|
|
@ -705,7 +713,7 @@ LayerPropertiesNode::operator== (const LayerPropertiesNode &d) const
|
|||
if (! LayerProperties::operator== (d)) {
|
||||
return false;
|
||||
}
|
||||
return m_children == d.m_children;
|
||||
return m_children == d.m_children && m_expanded == d.m_expanded;
|
||||
}
|
||||
|
||||
LayoutViewBase *LayerPropertiesNode::view() const
|
||||
|
|
@ -713,6 +721,15 @@ LayoutViewBase *LayerPropertiesNode::view() const
|
|||
return const_cast<lay::LayoutViewBase *> (mp_view.get ());
|
||||
}
|
||||
|
||||
void
|
||||
LayerPropertiesNode::set_expanded (bool ex)
|
||||
{
|
||||
if (expanded () != ex) {
|
||||
m_expanded = ex;
|
||||
expanded_state_changed ();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int
|
||||
LayerPropertiesNode::list_index () const
|
||||
{
|
||||
|
|
@ -740,6 +757,12 @@ LayerPropertiesNode::realize_source () const
|
|||
do_realize (mp_view.get ());
|
||||
}
|
||||
|
||||
void
|
||||
LayerPropertiesNode::expanded_state_changed ()
|
||||
{
|
||||
touch ();
|
||||
}
|
||||
|
||||
void
|
||||
LayerPropertiesNode::need_realize (unsigned int flags, bool force)
|
||||
{
|
||||
|
|
@ -1763,8 +1786,9 @@ struct LineStyleIndexConverter
|
|||
static const tl::XMLElementList layer_element = tl::XMLElementList (
|
||||
// HINT: these make_member calls want to be qualified: otherwise an internal error
|
||||
// was observed ..
|
||||
tl::make_member<tl::color_t, LayerPropertiesNode> (&LayerPropertiesNode::frame_color_loc, &LayerPropertiesNode::set_frame_color_code, "frame-color", UIntColorConverter ()) +
|
||||
tl::make_member<tl::color_t, LayerPropertiesNode> (&LayerPropertiesNode::fill_color_loc, &LayerPropertiesNode::set_fill_color_code, "fill-color", UIntColorConverter ()) +
|
||||
tl::make_member<bool, LayerPropertiesNode> (&LayerPropertiesNode::expanded, &LayerPropertiesNode::set_expanded, "expanded") +
|
||||
tl::make_member<tl::color_t, LayerPropertiesNode> (&LayerPropertiesNode::frame_color_loc, &LayerPropertiesNode::set_frame_color_code, "frame-color", UIntColorConverter ()) +
|
||||
tl::make_member<tl::color_t, LayerPropertiesNode> (&LayerPropertiesNode::fill_color_loc, &LayerPropertiesNode::set_fill_color_code, "fill-color", UIntColorConverter ()) +
|
||||
tl::make_member<int, LayerPropertiesNode> (&LayerPropertiesNode::frame_brightness_loc, &LayerPropertiesNode::set_frame_brightness, "frame-brightness") +
|
||||
tl::make_member<int, LayerPropertiesNode> (&LayerPropertiesNode::fill_brightness_loc, &LayerPropertiesNode::set_fill_brightness, "fill-brightness") +
|
||||
tl::make_member<int, LayerPropertiesNode> (&LayerPropertiesNode::dither_pattern_loc, &LayerPropertiesNode::set_dither_pattern, "dither-pattern", DitherPatternIndexConverter ()) +
|
||||
|
|
@ -2061,6 +2085,16 @@ LayerPropertiesNodeRef::need_realize (unsigned int flags, bool force)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerPropertiesNodeRef::expanded_state_changed ()
|
||||
{
|
||||
LayerPropertiesNode::expanded_state_changed ();
|
||||
|
||||
if (is_valid ()) {
|
||||
view ()->set_layer_node_expanded (m_iter, expanded ());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerPropertiesNodeRef::refresh () const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -919,6 +919,11 @@ protected:
|
|||
*/
|
||||
virtual void need_realize (unsigned int flags, bool force = false);
|
||||
|
||||
/**
|
||||
* @brief indicates a change of the collapsed/expanded state
|
||||
*/
|
||||
virtual void expanded_state_changed ();
|
||||
|
||||
/**
|
||||
* @brief Fetches the current status from the original properties for the LayerPropertiesNodeRef implementation
|
||||
*/
|
||||
|
|
@ -1069,6 +1074,20 @@ public:
|
|||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the expanded state of the layer properties tree node
|
||||
*/
|
||||
void set_expanded (bool ex);
|
||||
|
||||
/**
|
||||
* @brief Gets the expanded state of the layer properties node
|
||||
*/
|
||||
bool expanded () const
|
||||
{
|
||||
refresh ();
|
||||
return m_expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Child layers: begin iterator
|
||||
*/
|
||||
|
|
@ -1207,14 +1226,21 @@ public:
|
|||
virtual void realize_source () const;
|
||||
virtual void realize_visual () const;
|
||||
|
||||
void set_expanded_silent (bool ex)
|
||||
{
|
||||
m_expanded = ex;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void need_realize (unsigned int flags, bool force);
|
||||
virtual void expanded_state_changed ();
|
||||
void set_parent (const LayerPropertiesNode *);
|
||||
|
||||
private:
|
||||
// A reference to the view
|
||||
tl::weak_ptr<lay::LayoutViewBase> mp_view;
|
||||
unsigned int m_list_index;
|
||||
bool m_expanded;
|
||||
// the parent node
|
||||
tl::weak_ptr<LayerPropertiesNode> mp_parent;
|
||||
// the list of children
|
||||
|
|
@ -2009,8 +2035,9 @@ private:
|
|||
tl::weak_ptr<LayerPropertiesNode> mp_node;
|
||||
size_t m_synched_gen_id;
|
||||
|
||||
void need_realize (unsigned int flags, bool force);
|
||||
void refresh () const;
|
||||
virtual void need_realize (unsigned int flags, bool force);
|
||||
virtual void expanded_state_changed ();
|
||||
virtual void refresh () const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1880,6 +1880,21 @@ LayoutViewBase::replace_layer_node (unsigned int index, const LayerPropertiesCon
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewBase::set_layer_node_expanded (unsigned int index, const LayerPropertiesConstIterator &iter, bool ex)
|
||||
{
|
||||
if (ex != iter->expanded ()) {
|
||||
|
||||
LayerPropertiesIterator non_const_iter (get_properties (index), iter.uint ());
|
||||
non_const_iter->set_expanded (ex);
|
||||
|
||||
if (index == current_layer_list ()) {
|
||||
layer_list_changed_event (8 /*expanded state needs update*/);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayoutViewBase::set_properties (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerProperties &props)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -335,6 +335,17 @@ public:
|
|||
set_properties (current_layer_list (), iter, props);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the given node is expanded in the layer tree
|
||||
*
|
||||
* @param iter Points to the layer node to be modified
|
||||
* @param ex True if the layer node shall be expanded, false if it shall be collapsed
|
||||
*/
|
||||
void set_layer_node_expanded (const LayerPropertiesConstIterator &iter, bool ex)
|
||||
{
|
||||
set_layer_node_expanded (current_layer_list (), iter, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the layer properties of a layer with the given position (by iterator) for the layer list with the given index
|
||||
*
|
||||
|
|
@ -344,6 +355,15 @@ public:
|
|||
*/
|
||||
void set_properties (unsigned int index, const LayerPropertiesConstIterator &iter, const LayerProperties &props);
|
||||
|
||||
/**
|
||||
* @brief Sets a value indicating whether the given node is expanded in the layer tree
|
||||
*
|
||||
* @param index The layer list's index
|
||||
* @param iter Points to the layer node to be modified
|
||||
* @param ex True if the layer node shall be expanded, false if it shall be collapsed
|
||||
*/
|
||||
void set_layer_node_expanded (unsigned int index, const LayerPropertiesConstIterator &iter, bool ex);
|
||||
|
||||
/**
|
||||
* @brief Expand the layer properties of all tabs
|
||||
*
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ TEST (1)
|
|||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
||||
"<layer-properties>\n"
|
||||
" <properties>\n"
|
||||
" <expanded>false</expanded>\n"
|
||||
" <frame-color/>\n"
|
||||
" <fill-color/>\n"
|
||||
" <frame-brightness>0</frame-brightness>\n"
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ LayerControlPanel::LayerControlPanel (lay::LayoutViewBase *view, db::Manager *ma
|
|||
db::Object (manager),
|
||||
mp_view (view),
|
||||
m_needs_update (true),
|
||||
m_expanded_state_needs_update (false),
|
||||
m_tabs_need_update (true),
|
||||
m_hidden_flags_need_update (true),
|
||||
m_in_update (false),
|
||||
|
|
@ -1696,6 +1697,7 @@ LayerControlPanel::cancel_updates ()
|
|||
{
|
||||
m_in_update = false;
|
||||
m_needs_update = false;
|
||||
m_expanded_state_needs_update = false;
|
||||
m_hidden_flags_need_update = false;
|
||||
m_tabs_need_update = false;
|
||||
}
|
||||
|
|
@ -1874,11 +1876,13 @@ LayerControlPanel::do_update_content ()
|
|||
}
|
||||
|
||||
if (m_hidden_flags_need_update) {
|
||||
|
||||
do_update_hidden_flags ();
|
||||
|
||||
m_hidden_flags_need_update = false;
|
||||
}
|
||||
|
||||
if (m_expanded_state_needs_update) {
|
||||
restore_expanded ();
|
||||
m_expanded_state_needs_update = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1993,6 +1997,11 @@ LayerControlPanel::signal_li_changed (int)
|
|||
void
|
||||
LayerControlPanel::update_required (int f)
|
||||
{
|
||||
// the name of a layer list has changed
|
||||
if ((f & 8) != 0) {
|
||||
m_expanded_state_needs_update = true;
|
||||
}
|
||||
|
||||
// the name of a layer list has changed
|
||||
if ((f & 4) != 0) {
|
||||
m_tabs_need_update = true;
|
||||
|
|
@ -2032,18 +2041,18 @@ LayerControlPanel::current_index_changed (const QModelIndex &index)
|
|||
void
|
||||
LayerControlPanel::group_collapsed (const QModelIndex &index)
|
||||
{
|
||||
lay::LayerPropertiesConstIterator iter = mp_model->iterator (index);
|
||||
auto iter = mp_model->iterator_nc (index);
|
||||
if (! iter.is_null () && ! iter.at_end ()) {
|
||||
m_expanded.erase (iter->id ());
|
||||
iter->set_expanded_silent (false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LayerControlPanel::group_expanded (const QModelIndex &index)
|
||||
{
|
||||
lay::LayerPropertiesConstIterator iter = mp_model->iterator (index);
|
||||
auto iter = mp_model->iterator_nc (index);
|
||||
if (! iter.is_null () && ! iter.at_end ()) {
|
||||
m_expanded.insert (iter->id ());
|
||||
iter->set_expanded_silent (true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2052,33 +2061,16 @@ LayerControlPanel::restore_expanded ()
|
|||
{
|
||||
mp_layer_list->blockSignals (true);
|
||||
|
||||
#if 1
|
||||
// By keeping m_expanded, we can preserve the expansion state of different tabs.
|
||||
// However we will always continue filling m_expanded.
|
||||
lay::LayerPropertiesConstIterator l = mp_view->begin_layers ();
|
||||
while (! l.at_end ()) {
|
||||
if (m_expanded.find (l->id ()) != m_expanded.end ()) {
|
||||
QModelIndex index = mp_model->index (l, 0);
|
||||
QModelIndex index = mp_model->index (l, 0);
|
||||
if (l->expanded ()) {
|
||||
mp_layer_list->expand (index);
|
||||
} else {
|
||||
mp_layer_list->collapse (index);
|
||||
}
|
||||
++l;
|
||||
}
|
||||
#else
|
||||
// this solution will forget the other tab's expansion flags.
|
||||
std::set<unsigned int> new_expanded;
|
||||
|
||||
lay::LayerPropertiesConstIterator l = mp_view->begin_layers ();
|
||||
while (! l.at_end ()) {
|
||||
if (m_expanded.find (l->id ()) != m_expanded.end ()) {
|
||||
new_expanded.insert (l->id ());
|
||||
QModelIndex index = mp_model->index (l, 0);
|
||||
mp_layer_list->expand (index);
|
||||
}
|
||||
++l;
|
||||
}
|
||||
|
||||
m_expanded.swap (new_expanded);
|
||||
#endif
|
||||
|
||||
mp_layer_list->blockSignals (false);
|
||||
}
|
||||
|
|
@ -2287,7 +2279,7 @@ public:
|
|||
|
||||
menu_entries.push_back (lay::menu_item ("cm_lv_select_all", "select_all", at, tl::to_string (QObject::tr ("Select All"))));
|
||||
// It is not sure, whether "expandAll" destabilizes the tree widget:
|
||||
// menu_entries.push_back (lay::menu_item ("cm_lv_expand_all", "expand_all", at, tl::to_string (QObject::tr ("Expand All")));
|
||||
// menu_entries.push_back (lay::menu_item ("cm_lv_expand_all", "expand_all", at, tl::to_string (QObject::tr ("Expand All"))));
|
||||
menu_entries.push_back (lay::separator ("tab_group", at));
|
||||
menu_entries.push_back (lay::submenu ("tab_menu", at, tl::to_string (QObject::tr ("Tabs"))));
|
||||
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ private:
|
|||
LayerTreeModel *mp_model;
|
||||
lay::LayoutViewBase *mp_view;
|
||||
bool m_needs_update;
|
||||
bool m_expanded_state_needs_update;
|
||||
bool m_tabs_need_update;
|
||||
bool m_hidden_flags_need_update;
|
||||
bool m_in_update;
|
||||
|
|
@ -360,7 +361,6 @@ private:
|
|||
int m_oversampling;
|
||||
bool m_hrm;
|
||||
tl::DeferredMethod<LayerControlPanel> m_do_update_content_dm;
|
||||
std::set<unsigned int> m_expanded;
|
||||
bool m_no_stipples;
|
||||
QLabel *m_no_stipples_label;
|
||||
lay::DecoratedLineEdit *mp_search_edit_box;
|
||||
|
|
|
|||
|
|
@ -756,7 +756,20 @@ LayerTreeModel::iterator (const QModelIndex &index) const
|
|||
return lay::LayerPropertiesConstIterator ();
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
lay::LayerPropertiesIterator
|
||||
LayerTreeModel::iterator_nc (const QModelIndex &index)
|
||||
{
|
||||
if (index.isValid ()) {
|
||||
size_t iter_index = size_t (index.internalPointer ());
|
||||
if (mp_view->layer_lists () > 0 && iter_index >= m_id_start && iter_index < m_id_end) {
|
||||
return lay::LayerPropertiesIterator (mp_view->get_properties (), iter_index - m_id_start);
|
||||
}
|
||||
}
|
||||
|
||||
return lay::LayerPropertiesIterator ();
|
||||
}
|
||||
|
||||
QModelIndex
|
||||
LayerTreeModel::index (lay::LayerPropertiesConstIterator iter, int column) const
|
||||
{
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ namespace lay
|
|||
|
||||
class LayoutViewBase;
|
||||
class LayerPropertiesConstIterator;
|
||||
class LayerPropertiesIterator;
|
||||
|
||||
/**
|
||||
* @brief A helper class implementing a cache for the "test shapes in view" feature
|
||||
|
|
@ -107,6 +108,16 @@ public:
|
|||
virtual QModelIndex index (int row, int column, const QModelIndex &parent) const;
|
||||
virtual QModelIndex parent (const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the expanded state for a given model index
|
||||
*/
|
||||
void set_expanded (const QModelIndex &index, bool ex);
|
||||
|
||||
/**
|
||||
* @brief Gets the expanded state for a given model index
|
||||
*/
|
||||
bool expanded (const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @brief Provides an icon for a given layer style
|
||||
*/
|
||||
|
|
@ -127,6 +138,11 @@ public:
|
|||
*/
|
||||
lay::LayerPropertiesConstIterator iterator (const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @brief Converts a QModelIndex to an iterator (non-const)
|
||||
*/
|
||||
lay::LayerPropertiesIterator iterator_nc (const QModelIndex &index);
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating that an entry is hidden
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -782,7 +782,7 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool
|
|||
}
|
||||
}
|
||||
|
||||
while (test ("+")) {
|
||||
while ((in_subnet && ! at_end ()) || test ("+")) {
|
||||
|
||||
bool was_shield = false;
|
||||
unsigned int mask = 0;
|
||||
|
|
@ -820,6 +820,8 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool
|
|||
|
||||
} else {
|
||||
|
||||
bool any = false;
|
||||
|
||||
bool prefixed = false;
|
||||
bool can_have_rect_polygon_or_via = true;
|
||||
|
||||
|
|
@ -831,8 +833,6 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool
|
|||
can_have_rect_polygon_or_via = test ("+");
|
||||
}
|
||||
|
||||
bool any = false;
|
||||
|
||||
if (can_have_rect_polygon_or_via) {
|
||||
if (test ("SHAPE")) {
|
||||
take ();
|
||||
|
|
@ -882,22 +882,29 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool
|
|||
|
||||
} else if (can_have_rect_polygon_or_via && test ("VIA")) {
|
||||
|
||||
// For the via, the masks are encoded in a three-digit number (<mask-top> <mask-cut> <mask_bottom>)
|
||||
unsigned int mask_top = (mask / 100) % 10;
|
||||
unsigned int mask_cut = (mask / 10) % 10;
|
||||
unsigned int mask_bottom = mask % 10;
|
||||
|
||||
std::string vn = get ();
|
||||
db::FTrans ft = get_orient (true /*optional*/);
|
||||
|
||||
test ("(");
|
||||
db::Vector pt = get_vector (scale);
|
||||
test (")");
|
||||
while (test ("(")) {
|
||||
|
||||
std::map<std::string, ViaDesc>::const_iterator vd = m_via_desc.find (vn);
|
||||
if (vd != m_via_desc.end ()) {
|
||||
// TODO: no mask specification here?
|
||||
db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, 0, 0, 0, &m_lef_importer);
|
||||
if (cell) {
|
||||
design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), pt)));
|
||||
db::Vector pt = get_vector (scale);
|
||||
test (")");
|
||||
|
||||
std::map<std::string, ViaDesc>::const_iterator vd = m_via_desc.find (vn);
|
||||
if (vd != m_via_desc.end ()) {
|
||||
db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer);
|
||||
if (cell) {
|
||||
design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), pt)));
|
||||
}
|
||||
} else {
|
||||
warn (tl::to_string (tr ("Invalid via name: ")) + vn);
|
||||
}
|
||||
} else {
|
||||
error (tl::to_string (tr ("Invalid via name: ")) + vn);
|
||||
|
||||
}
|
||||
|
||||
any = true;
|
||||
|
|
@ -1120,9 +1127,9 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale)
|
|||
if (vd.m1.empty () && vd.m2.empty ()) {
|
||||
|
||||
// analyze the layers to find the metals
|
||||
if (routing_layers.size () == 2) {
|
||||
vd.m1 = routing_layers[0];
|
||||
vd.m2 = routing_layers[1];
|
||||
if (routing_layers.size () == 2 || routing_layers.size () == 1) {
|
||||
vd.m1 = routing_layers.front ();
|
||||
vd.m2 = routing_layers.back ();
|
||||
} else {
|
||||
warn (tl::to_string (tr ("Cannot determine routing layers for via: ")) + n);
|
||||
}
|
||||
|
|
@ -1204,7 +1211,7 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale)
|
|||
|
||||
double x = 0.0, y = 0.0;
|
||||
|
||||
while (! at_end () && ! test ("+") && ! test (";")) {
|
||||
while (! at_end () && ! peek ("+") && ! peek (";")) {
|
||||
|
||||
test ("(");
|
||||
if (! test ("*")) {
|
||||
|
|
@ -1237,8 +1244,36 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale)
|
|||
|
||||
} else if (test ("VIA")) {
|
||||
|
||||
// TODO: implement
|
||||
error (tl::to_string (tr ("VIA not supported on pins currently")));
|
||||
// TODO: clarify - VIA on pins is regarded VIA purpose, not PIN and gives a separate cell
|
||||
|
||||
std::string vn = get ();
|
||||
|
||||
unsigned int mask = 0;
|
||||
if (test ("MASK")) {
|
||||
mask = get_mask (get_long ());
|
||||
}
|
||||
|
||||
while (test ("(")) {
|
||||
|
||||
db::Vector pt = get_vector (scale);
|
||||
test (")");
|
||||
|
||||
unsigned int mask_top = (mask / 100) % 10;
|
||||
unsigned int mask_cut = (mask / 10) % 10;
|
||||
unsigned int mask_bottom = mask % 10;
|
||||
|
||||
std::map<std::string, ViaDesc>::const_iterator vd = m_via_desc.find (vn);
|
||||
if (vd != m_via_desc.end ()) {
|
||||
std::string nondefaultrule;
|
||||
db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer);
|
||||
if (cell) {
|
||||
design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt)));
|
||||
}
|
||||
} else {
|
||||
warn (tl::to_string (tr ("Invalid via name: ")) + vn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
while (! peek ("+") && ! peek ("-") && ! peek (";")) {
|
||||
|
|
@ -1391,13 +1426,49 @@ DEFImporter::read_fills (db::Layout &layout, db::Cell &design, double scale)
|
|||
|
||||
} else if (test ("VIA")) {
|
||||
|
||||
// TODO: implement
|
||||
warn (tl::to_string (tr ("VIA not supported on fills currently")));
|
||||
// TODO: clarify - VIA on fill is regarded VIA purpose, not PIN and gives a separate cell
|
||||
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
std::string vn = get ();
|
||||
|
||||
unsigned int mask = 0;
|
||||
while (test ("+")) {
|
||||
if (test ("MASK")) {
|
||||
mask = get_mask (get_long ());
|
||||
} else if (test ("OPC")) {
|
||||
// ignore
|
||||
} else {
|
||||
error (tl::to_string (tr ("Expected 'MASK' or 'OPC' inside fill/VIA definition")));
|
||||
}
|
||||
}
|
||||
|
||||
if (peek ("+") && test ("MASK")) {
|
||||
mask = get_mask (get_long ());
|
||||
}
|
||||
|
||||
unsigned int mask_top = (mask / 100) % 10;
|
||||
unsigned int mask_cut = (mask / 10) % 10;
|
||||
unsigned int mask_bottom = mask % 10;
|
||||
|
||||
while (test ("(")) {
|
||||
|
||||
db::Vector pt = get_vector (scale);
|
||||
test (")");
|
||||
|
||||
std::map<std::string, ViaDesc>::const_iterator vd = m_via_desc.find (vn);
|
||||
if (vd != m_via_desc.end ()) {
|
||||
std::string nondefaultrule;
|
||||
db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer);
|
||||
if (cell) {
|
||||
design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt)));
|
||||
}
|
||||
} else {
|
||||
warn (tl::to_string (tr ("Invalid via name: ")) + vn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test (";");
|
||||
|
||||
} else {
|
||||
error (tl::to_string (tr ("'LAYER' or 'VIA' keyword expected")));
|
||||
}
|
||||
|
|
@ -1432,7 +1503,7 @@ DEFImporter::read_styles (double scale)
|
|||
|
||||
}
|
||||
|
||||
m_styles.insert (std::make_pair (sn, db::Polygon ())).first->second.assign_hull (points.begin (), points.end ());
|
||||
m_styles.insert (std::make_pair (sn, db::Polygon ())).first->second.assign_hull (points.begin (), points.end (), false /*don't compress*/);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1990,7 +1990,11 @@ LEFDEFImporter::read (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderS
|
|||
void
|
||||
LEFDEFImporter::error (const std::string &msg)
|
||||
{
|
||||
throw LEFDEFReaderException (msg, int (mp_stream->line_number ()), m_cellname, m_fn);
|
||||
if (m_sections.empty ()) {
|
||||
throw LEFDEFReaderException (msg, int (mp_stream->line_number ()), m_cellname, m_fn);
|
||||
} else {
|
||||
throw LEFDEFReaderException (msg + tl::sprintf (tl::to_string (tr (" (inside %s)")), tl::join (m_sections, "/")), int (mp_stream->line_number ()), m_cellname, m_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2140,6 +2144,18 @@ LEFDEFImporter::get ()
|
|||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
LEFDEFImporter::enter_section (const std::string &name)
|
||||
{
|
||||
m_sections.push_back (name);
|
||||
}
|
||||
|
||||
void
|
||||
LEFDEFImporter::leave_section ()
|
||||
{
|
||||
m_sections.pop_back ();
|
||||
}
|
||||
|
||||
const std::string &
|
||||
LEFDEFImporter::next ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1706,8 +1706,31 @@ private:
|
|||
db::property_names_id_type m_pin_prop_name_id;
|
||||
db::LEFDEFReaderOptions m_options;
|
||||
int m_warn_level;
|
||||
std::vector<std::string> m_sections;
|
||||
|
||||
friend class LEFDEFSection;
|
||||
|
||||
const std::string &next ();
|
||||
void enter_section (const std::string &name);
|
||||
void leave_section ();
|
||||
};
|
||||
|
||||
class DB_PLUGIN_PUBLIC LEFDEFSection
|
||||
{
|
||||
public:
|
||||
LEFDEFSection (LEFDEFImporter *importer, const std::string &name)
|
||||
: mp_importer (importer)
|
||||
{
|
||||
mp_importer->enter_section (name);
|
||||
}
|
||||
|
||||
~LEFDEFSection ()
|
||||
{
|
||||
mp_importer->leave_section ();
|
||||
}
|
||||
|
||||
private:
|
||||
LEFDEFImporter *mp_importer;
|
||||
};
|
||||
|
||||
class DB_PLUGIN_PUBLIC LEFDEFReader
|
||||
|
|
|
|||
|
|
@ -388,9 +388,7 @@ LEFImporter::read_geometries (GeometryBasedLayoutGenerator *lg, double dbu, Laye
|
|||
} else if (test ("PROPERTY")) {
|
||||
|
||||
// skip properties
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
|
||||
} else {
|
||||
// stop at unknown token
|
||||
|
|
@ -406,22 +404,20 @@ LEFImporter::read_nondefaultrule (db::Layout &layout)
|
|||
// read NONDEFAULTRULE sections
|
||||
std::string n = get ();
|
||||
|
||||
while (! test ("END") || ! test (n)) {
|
||||
while (! at_end () && ! test ("END")) {
|
||||
|
||||
if (test ("LAYER")) {
|
||||
|
||||
std::string l = get ();
|
||||
|
||||
// read the width for the layer
|
||||
while (! test ("END")) {
|
||||
while (! at_end () && ! test ("END")) {
|
||||
if (test ("WIDTH")) {
|
||||
double w = get_double ();
|
||||
test (";");
|
||||
m_nondefault_widths[n][l] = std::make_pair (w, w);
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -432,18 +428,31 @@ LEFImporter::read_nondefaultrule (db::Layout &layout)
|
|||
read_viadef (layout, n);
|
||||
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
|
||||
std::string token = get ();
|
||||
|
||||
if (token == "SPACING") {
|
||||
// read over sections we do not need
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
test (token);
|
||||
} else if (token != ";") {
|
||||
// read over lines we do not need
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test (n);
|
||||
}
|
||||
|
||||
void
|
||||
LEFImporter::read_viadef_by_rule (RuleBasedViaGenerator *vg, ViaDesc &via_desc, const std::string & /*n*/, double dbu)
|
||||
{
|
||||
while (! test ("END")) {
|
||||
while (! at_end () && ! test ("END")) {
|
||||
|
||||
double x, y;
|
||||
|
||||
|
|
@ -557,6 +566,13 @@ LEFImporter::read_viadef_by_geometry (GeometryBasedLayoutGenerator *lg, ViaDesc
|
|||
get_double ();
|
||||
test (";");
|
||||
|
||||
} else if (test ("FOREIGN")) {
|
||||
|
||||
// undocumented
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
|
||||
} else if (test ("LAYER")) {
|
||||
|
||||
layer_name = get ();
|
||||
|
|
@ -631,9 +647,7 @@ LEFImporter::read_viadef_by_geometry (GeometryBasedLayoutGenerator *lg, ViaDesc
|
|||
} else if (test ("PROPERTY")) {
|
||||
|
||||
// skip properties
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
|
||||
} else {
|
||||
// stop at unknown token
|
||||
|
|
@ -663,7 +677,7 @@ LEFImporter::read_viadef (Layout &layout, const std::string &nondefaultrule)
|
|||
|
||||
ViaDesc &via_desc = m_vias[n];
|
||||
|
||||
while (test ("DEFAULT") || test ("TOPOFSTACKONLY"))
|
||||
while (test ("DEFAULT") || test ("TOPOFSTACKONLY") || test("GENERATED"))
|
||||
;
|
||||
test (";");
|
||||
|
||||
|
|
@ -746,55 +760,51 @@ LEFImporter::read_layer (Layout & /*layout*/)
|
|||
// blocks following a semicolon
|
||||
take ();
|
||||
if (test ("FREQUENCY")) {
|
||||
while (! test ("TABLEENTRIES")) {
|
||||
while (! at_end () && ! test ("TABLEENTRIES")) {
|
||||
take ();
|
||||
}
|
||||
}
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
|
||||
} else if (test ("PROPERTY")) {
|
||||
|
||||
std::string name = get ();
|
||||
tl::Variant value = get ();
|
||||
while (! test (";") && ! at_end ()) {
|
||||
|
||||
if (name == "LEF58_MINWIDTH") {
|
||||
std::string name = get ();
|
||||
tl::Variant value = get ();
|
||||
|
||||
// Cadence extension
|
||||
tl::Extractor ex (value.to_string ());
|
||||
double v = 0.0;
|
||||
if (ex.test ("MINWIDTH") && ex.try_read (v)) {
|
||||
if (ex.test ("WRONGDIRECTION")) {
|
||||
wmin_wrongdir = v;
|
||||
} else {
|
||||
wmin = v;
|
||||
if (name == "LEF58_MINWIDTH") {
|
||||
|
||||
// Cadence extension
|
||||
tl::Extractor ex (value.to_string ());
|
||||
double v = 0.0;
|
||||
if (ex.test ("MINWIDTH") && ex.try_read (v)) {
|
||||
if (ex.test ("WRONGDIRECTION")) {
|
||||
wmin_wrongdir = v;
|
||||
} else {
|
||||
wmin = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (name == "LEF58_WIDTH") {
|
||||
} else if (name == "LEF58_WIDTH") {
|
||||
|
||||
// Cadence extension
|
||||
tl::Extractor ex (value.to_string ());
|
||||
double v = 0.0;
|
||||
if (ex.test ("WIDTH") && ex.try_read (v)) {
|
||||
if (ex.test ("WRONGDIRECTION")) {
|
||||
w_wrongdir = v;
|
||||
} else {
|
||||
w = v;
|
||||
// Cadence extension
|
||||
tl::Extractor ex (value.to_string ());
|
||||
double v = 0.0;
|
||||
if (ex.test ("WIDTH") && ex.try_read (v)) {
|
||||
if (ex.test ("WRONGDIRECTION")) {
|
||||
w_wrongdir = v;
|
||||
} else {
|
||||
w = v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
expect (";");
|
||||
|
||||
} else {
|
||||
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
|
||||
skip_entry ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -865,6 +875,8 @@ LEFImporter::read_macro (Layout &layout)
|
|||
|
||||
} else if (test ("PIN")) {
|
||||
|
||||
LEFDEFSection section (this, "PIN");
|
||||
|
||||
std::string pn = get ();
|
||||
std::string dir;
|
||||
|
||||
|
|
@ -881,6 +893,8 @@ LEFImporter::read_macro (Layout &layout)
|
|||
|
||||
} else if (test ("PORT")) {
|
||||
|
||||
LEFDEFSection section (this, "PORT");
|
||||
|
||||
// produce pin labels
|
||||
// TODO: put a label on every single object?
|
||||
std::string label = pn;
|
||||
|
|
@ -916,9 +930,7 @@ LEFImporter::read_macro (Layout &layout)
|
|||
expect ("END");
|
||||
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -926,12 +938,19 @@ LEFImporter::read_macro (Layout &layout)
|
|||
|
||||
} else if (test ("FOREIGN")) {
|
||||
|
||||
LEFDEFSection section (this, "FOREIGN");
|
||||
|
||||
std::string cn = get ();
|
||||
|
||||
db::Point vec;
|
||||
db::FTrans ft;
|
||||
if (! peek (";")) {
|
||||
vec = get_point (1.0 / layout.dbu ());
|
||||
if (test ("(")) {
|
||||
vec = get_point (1.0 / layout.dbu ());
|
||||
expect (")");
|
||||
} else {
|
||||
vec = get_point (1.0 / layout.dbu ());
|
||||
}
|
||||
ft = get_orient (true);
|
||||
}
|
||||
|
||||
|
|
@ -940,7 +959,7 @@ LEFImporter::read_macro (Layout &layout)
|
|||
if (options ().macro_resolution_mode () != 1) {
|
||||
|
||||
if (! foreign_name.empty ()) {
|
||||
error (tl::to_string (tr ("Duplicate FOREIGN definition")));
|
||||
warn (tl::to_string (tr ("Duplicate FOREIGN definition")));
|
||||
}
|
||||
|
||||
// What is the definition of the FOREIGN transformation?
|
||||
|
|
@ -956,6 +975,8 @@ LEFImporter::read_macro (Layout &layout)
|
|||
|
||||
} else if (test ("OBS")) {
|
||||
|
||||
LEFDEFSection section (this, "OBS");
|
||||
|
||||
if (reader_state ()->tech_comp ()->produce_obstructions ()) {
|
||||
read_geometries (mg, layout.dbu (), Obstructions);
|
||||
} else {
|
||||
|
|
@ -966,8 +987,10 @@ LEFImporter::read_macro (Layout &layout)
|
|||
|
||||
} else if (test ("DENSITY")) {
|
||||
|
||||
LEFDEFSection section (this, "DENSITY");
|
||||
|
||||
// read over DENSITY statements
|
||||
while (! test ("END")) {
|
||||
while (! at_end () && ! test ("END")) {
|
||||
if (test ("LAYER")) {
|
||||
get ();
|
||||
expect (";");
|
||||
|
|
@ -980,17 +1003,27 @@ LEFImporter::read_macro (Layout &layout)
|
|||
}
|
||||
}
|
||||
|
||||
expect ("END");
|
||||
|
||||
} else if (test ("FIXEDMASK")) {
|
||||
|
||||
mg->set_fixedmask (true);
|
||||
expect (";");
|
||||
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
|
||||
std::string token = get ();
|
||||
LEFDEFSection section (this, token);
|
||||
|
||||
if (token == "TIMING") {
|
||||
// read over sections we do not need
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
test (token);
|
||||
} else if (token != ";") {
|
||||
// read over lines we do not need
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1031,17 +1064,16 @@ LEFImporter::do_read (db::Layout &layout)
|
|||
|
||||
} else if (test ("UNITS")) {
|
||||
|
||||
// read over SPACING sections
|
||||
while (! test ("END")) {
|
||||
LEFDEFSection section (this, "UNITS");
|
||||
|
||||
while (! at_end () && ! test ("END")) {
|
||||
if (test ("DATABASE")) {
|
||||
expect ("MICRONS");
|
||||
// TODO: what to do with that value
|
||||
/* dbu_mic = */ get_double ();
|
||||
expect (";");
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
skip_entry ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1049,66 +1081,138 @@ LEFImporter::do_read (db::Layout &layout)
|
|||
|
||||
} else if (test ("SPACING")) {
|
||||
|
||||
LEFDEFSection section (this, "SPACING");
|
||||
|
||||
// read over SPACING sections
|
||||
while (! test ("END") || ! test ("SPACING")) {
|
||||
take ();
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test ("SPACING");
|
||||
|
||||
} else if (test ("PROPERTYDEFINITIONS")) {
|
||||
|
||||
LEFDEFSection section (this, "PROPERTYDEFINITIONS");
|
||||
|
||||
// read over PROPERTYDEFINITIONS sections
|
||||
while (! test ("END") || ! test ("PROPERTYDEFINITIONS")) {
|
||||
take ();
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test ("PROPERTYDEFINITIONS");
|
||||
|
||||
} else if (test ("NONDEFAULTRULE")) {
|
||||
|
||||
LEFDEFSection section (this, "NONDEFAULTRULE");
|
||||
|
||||
read_nondefaultrule (layout);
|
||||
|
||||
} else if (test ("SITE")) {
|
||||
|
||||
// read over SITE sections
|
||||
LEFDEFSection section (this, "NONDEFAULTRULE");
|
||||
|
||||
// read over SITE or VIARULE sections
|
||||
std::string n = get ();
|
||||
while (! test ("END") || ! test (n)) {
|
||||
take ();
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test (n);
|
||||
|
||||
} else if (test ("VIARULE")) {
|
||||
|
||||
// read over VIARULE sections
|
||||
LEFDEFSection section (this, "VIARULE");
|
||||
|
||||
// read over SITE or VIARULE sections
|
||||
std::string n = get ();
|
||||
while (! test ("END") || ! test (n)) {
|
||||
take ();
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test (n);
|
||||
|
||||
} else if (test ("NOISETABLE")) {
|
||||
|
||||
LEFDEFSection section (this, "NOISETABLE");
|
||||
|
||||
// read over NOISETABLE sections
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test ("NOISETABLE");
|
||||
|
||||
} else if (test ("IRDROP")) {
|
||||
|
||||
LEFDEFSection section (this, "IRDROP");
|
||||
|
||||
// read over IRDROP sections
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
|
||||
test ("IRDROP");
|
||||
|
||||
} else if (test ("ARRAY")) {
|
||||
|
||||
LEFDEFSection section (this, "ARRAY");
|
||||
|
||||
// read over ARRAY sections
|
||||
std::string n = get ();
|
||||
while (! at_end () && ! test ("END")) {
|
||||
if (test ("FLOORPLAN")) {
|
||||
while (! at_end () && ! test ("END")) {
|
||||
skip_entry ();
|
||||
}
|
||||
} else {
|
||||
skip_entry ();
|
||||
}
|
||||
}
|
||||
|
||||
test (n);
|
||||
|
||||
} else if (test ("VIA")) {
|
||||
|
||||
LEFDEFSection section (this, "VIA");
|
||||
read_viadef (layout, std::string ());
|
||||
|
||||
} else if (test ("BEGINEXT")) {
|
||||
|
||||
LEFDEFSection section (this, "BEGINEXT");
|
||||
|
||||
// read over BEGINEXT sections
|
||||
while (! test ("ENDEXT")) {
|
||||
while (! at_end () && ! test ("ENDEXT")) {
|
||||
take ();
|
||||
}
|
||||
|
||||
} else if (test ("LAYER")) {
|
||||
|
||||
LEFDEFSection section (this, "LAYER");
|
||||
read_layer (layout);
|
||||
|
||||
} else if (test ("MACRO")) {
|
||||
|
||||
LEFDEFSection section (this, "MACRO");
|
||||
read_macro (layout);
|
||||
|
||||
} else {
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
|
||||
// read over entries we do not need
|
||||
skip_entry ();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LEFImporter::skip_entry ()
|
||||
{
|
||||
while (! at_end () && ! test (";")) {
|
||||
take ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LEFImporter::finish_lef (db::Layout &layout)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ private:
|
|||
void read_viadef_by_geometry (GeometryBasedLayoutGenerator *lg, ViaDesc &desc, const std::string &n, double dbu);
|
||||
void read_layer (Layout &layout);
|
||||
void read_macro (Layout &layout);
|
||||
void skip_entry ();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,81 +277,88 @@ static void run_test2 (tl::TestBase *_this, const char *lef_dir, const char *fil
|
|||
}
|
||||
}
|
||||
|
||||
TEST(1)
|
||||
TEST(lef1)
|
||||
{
|
||||
run_test (_this, "lef1", "lef:in.lef", 0, default_options ());
|
||||
}
|
||||
|
||||
TEST(2)
|
||||
TEST(lef2)
|
||||
{
|
||||
// Also tests ability of plugin to properly read LEF
|
||||
run_test (_this, "lef2", "read:in.lef", "au.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(3)
|
||||
TEST(lef3)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_cell_outline_layer ("OUTLINE (2/0)");
|
||||
run_test (_this, "lef3", "lef:in.lef", "au.oas.gz", options);
|
||||
}
|
||||
|
||||
TEST(4)
|
||||
TEST(lef4)
|
||||
{
|
||||
run_test (_this, "lef4", "lef:in.lef", 0, default_options ());
|
||||
}
|
||||
|
||||
TEST(5)
|
||||
TEST(lef5)
|
||||
{
|
||||
run_test (_this, "lef5", "lef:in.lef", 0, default_options ());
|
||||
}
|
||||
|
||||
TEST(6)
|
||||
TEST(lef6)
|
||||
{
|
||||
run_test (_this, "lef6", "lef:in.lef", 0, default_options ());
|
||||
}
|
||||
|
||||
TEST(7)
|
||||
TEST(lef7)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_cell_outline_layer ("OUTLINE (8/0)");
|
||||
run_test (_this, "lef7", "lef:in_tech.lef+lef:in.lef", "au.oas.gz", options);
|
||||
}
|
||||
|
||||
TEST(10)
|
||||
TEST(lef8)
|
||||
{
|
||||
// this is rather a smoke test and throws a number of warnings
|
||||
// (complete example)
|
||||
run_test (_this, "lef8", "lef:tech.lef+lef:a.lef", "au.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(def1)
|
||||
{
|
||||
run_test (_this, "def1", "lef:in.lef+def:in.def", "au2.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(11)
|
||||
TEST(def2)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_cell_outline_layer ("OUTLINE (10/0)");
|
||||
run_test (_this, "def2", "lef:0.lef+lef:1.lef+def:in.def.gz", "au.oas.gz", options);
|
||||
}
|
||||
|
||||
TEST(12)
|
||||
TEST(def3)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_cell_outline_layer ("OUTLINE (13/0)");
|
||||
run_test (_this, "def3", "lef:in.lef+def:in.def", "au.oas.gz", options);
|
||||
}
|
||||
|
||||
TEST(13)
|
||||
TEST(def4)
|
||||
{
|
||||
run_test (_this, "def4", "lef:in.lef+def:in.def", "au2.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(14)
|
||||
TEST(def5)
|
||||
{
|
||||
run_test (_this, "def5", "lef:in.lef+def:in.def", "au.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(15)
|
||||
TEST(def6)
|
||||
{
|
||||
run_test (_this, "def6", "lef:cells.lef+lef:tech.lef+def:in.def.gz", "au-new.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(16)
|
||||
TEST(def7)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_placement_blockage_layer ("PLACEMENT_BLK (11/0)");
|
||||
|
|
@ -361,12 +368,12 @@ TEST(16)
|
|||
run_test (_this, "def7", "map:in.map+lef:cells.lef+lef:tech.lef+def:in.def.gz", "au2_with_map_file-new.oas.gz", options);
|
||||
}
|
||||
|
||||
TEST(17)
|
||||
TEST(def8)
|
||||
{
|
||||
run_test (_this, "def8", "lef:tech.lef+def:in.def", "au.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(18)
|
||||
TEST(def9)
|
||||
{
|
||||
db::LEFDEFReaderOptions options = default_options ();
|
||||
options.set_separate_groups (true);
|
||||
|
|
@ -375,47 +382,56 @@ TEST(18)
|
|||
run_test (_this, "def9", "lef:tech.lef+lef:cells_modified.lef+def:in.def", "au_nogroups-new.oas.gz", default_options ());
|
||||
}
|
||||
|
||||
TEST(19)
|
||||
TEST(def10)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_cell_outline_layer ("OUTLINE (2/0)");
|
||||
run_test (_this, "def10", "def:in.def", "au.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(20)
|
||||
TEST(def11)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_cell_outline_layer ("OUTLINE (12/0)");
|
||||
run_test (_this, "def11", "lef:test.lef+def:test.def", "au.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(21)
|
||||
TEST(def12)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_cell_outline_layer ("OUTLINE (20/0)");
|
||||
run_test (_this, "def12", "lef:test.lef+def:test.def", "au-new.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(22)
|
||||
TEST(def13)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
run_test (_this, "def13", "map:test.map+lef:test.lef_5.8+def:top.def.gz", "au2.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(23)
|
||||
TEST(def14)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_macro_resolution_mode (1);
|
||||
run_test (_this, "def14", "map:test.map+lef:tech.lef+lef:stdlib.lef+def:test.def", "au.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(24)
|
||||
TEST(def15)
|
||||
{
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_macro_resolution_mode (1);
|
||||
run_test (_this, "def15", "map:test.map+lef:tech.lef+def:test.def", "au2.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(def16)
|
||||
{
|
||||
// this is rather a smoke test
|
||||
// (complete example)
|
||||
db::LEFDEFReaderOptions opt = default_options ();
|
||||
opt.set_macro_resolution_mode (1);
|
||||
run_test (_this, "def16", "lef:a.lef+lef:tech.lef+def:a.def", "au.oas.gz", opt);
|
||||
}
|
||||
|
||||
TEST(100)
|
||||
{
|
||||
run_test (_this, "issue-172", "lef:in.lef+def:in.def", "au.oas.gz", default_options (), false);
|
||||
|
|
@ -954,3 +970,9 @@ TEST(205_lef_resistance)
|
|||
run_test (_this, "issue-1214", "read:merged.nom.lef", "au.oas.gz", lefdef_opt, false);
|
||||
}
|
||||
|
||||
// issue 1282
|
||||
TEST(206_lef_spacing)
|
||||
{
|
||||
run_test (_this, "issue-1282", "read:a.lef", 0, default_options (), false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,10 +106,10 @@ PYTHONTEST (dbLayoutToNetlist, "dbLayoutToNetlist.py")
|
|||
PYTHONTEST (dbLayoutVsSchematic, "dbLayoutVsSchematic.py")
|
||||
PYTHONTEST (dbNetlistCrossReference, "dbNetlistCrossReference.py")
|
||||
PYTHONTEST (layLayers, "layLayers.py")
|
||||
PYTHONTEST (layPixelBuffer, "layPixelBuffer.py")
|
||||
PYTHONTEST (tlTest, "tlTest.py")
|
||||
#if defined(HAVE_QT) && defined(HAVE_QTBINDINGS)
|
||||
PYTHONTEST (qtbinding, "qtbinding.py")
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
VERSION 5.4 ;
|
||||
|
||||
NAMESCASESENSITIVE ON ;
|
||||
BUSBITCHARS "[]" ;
|
||||
DIVIDERCHAR "/" ;
|
||||
NOWIREEXTENSIONATPIN OFF ;
|
||||
|
||||
UNITS
|
||||
DATABASE MICRONS 1000 ;
|
||||
END UNITS
|
||||
|
||||
NONDEFAULTRULE DOUBLESPACE
|
||||
LAYER M1
|
||||
WIDTH 0.16 ;
|
||||
SPACING 0.36 ;
|
||||
END M1
|
||||
|
||||
# klayout 0.28.5 lef reader get's confused by the SPACING in NONDEFAULTRULE
|
||||
SPACING
|
||||
SAMENET M1 M1 0.36 ;
|
||||
END SPACING
|
||||
|
||||
END DOUBLESPACE
|
||||
END LIBRARY
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -22,7 +22,8 @@ import dbLayoutToNetlist
|
|||
import dbLayoutVsSchematic
|
||||
import dbNetlistCrossReference
|
||||
import layLayers
|
||||
|
||||
import layPixelBuffer
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
for suite in [
|
||||
|
|
@ -37,7 +38,8 @@ if __name__ == '__main__':
|
|||
# unittest.TestLoader().loadTestsFromTestCase(dbLayoutToNetlist.DBLayoutToNetlistTests),
|
||||
# unittest.TestLoader().loadTestsFromTestCase(dbLayoutVsSchematic.DBLayoutVsSchematicTests),
|
||||
unittest.TestLoader().loadTestsFromTestCase(dbNetlistCrossReference.DBNetlistCrossReferenceTests),
|
||||
unittest.TestLoader().loadTestsFromTestCase(layLayers.LAYLayersTests)
|
||||
unittest.TestLoader().loadTestsFromTestCase(layLayers.LAYLayersTests),
|
||||
suite.addTests(loader.loadTestsFromTestCase(layPixelBuffer.LAYPixelBufferTests))
|
||||
]:
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class DBLayoutToNetlistTests(unittest.TestCase):
|
|||
self.assertEqual(l2n.internal_layout().top_cell().name, ly.top_cell().name)
|
||||
self.assertEqual(l2n.internal_top_cell().name, ly.top_cell().name)
|
||||
|
||||
self.assertNotEqual(l2n.layer_of(r), ly.layer(6, 0)) # would be a strange coincidence ...
|
||||
self.assertNotEqual(l2n.layer_of(r), ly.layer(6, 0)) # would be a strange coincidence ...
|
||||
|
||||
cm = l2n.const_cell_mapping_into(ly, ly.top_cell())
|
||||
for ci in range(0, l2n.internal_layout().cells()):
|
||||
|
|
@ -80,7 +80,7 @@ class DBLayoutToNetlistTests(unittest.TestCase):
|
|||
rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" )
|
||||
rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" )
|
||||
rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" )
|
||||
|
||||
|
||||
# Intra-layer
|
||||
l2n.connect(rmetal1)
|
||||
l2n.connect(rvia1)
|
||||
|
|
@ -92,7 +92,7 @@ class DBLayoutToNetlistTests(unittest.TestCase):
|
|||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist()
|
||||
|
||||
self.assertEqual(str(l2n.netlist()), """circuit TRANS ($1=$1,$2=$2);
|
||||
|
|
@ -137,7 +137,7 @@ end;
|
|||
self.assertEqual(",".join(a), "$2")
|
||||
self.assertEqual(str(t), "r0 *1 2.64,0")
|
||||
|
||||
self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)),
|
||||
self.assertEqual(str(l2n.shapes_of_net(n, rmetal1, True)),
|
||||
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
|
||||
|
||||
shapes = pya.Shapes()
|
||||
|
|
@ -145,7 +145,7 @@ end;
|
|||
r = pya.Region()
|
||||
for s in shapes.each():
|
||||
r.insert(s.polygon)
|
||||
self.assertEqual(str(r),
|
||||
self.assertEqual(str(r),
|
||||
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
|
||||
|
||||
def test_10_LayoutToNetlistExtractionWithoutDevices(self):
|
||||
|
|
@ -169,7 +169,7 @@ end;
|
|||
rvia1 = l2n.make_polygon_layer( ly.layer(7, 0), "via1" )
|
||||
rmetal2 = l2n.make_polygon_layer( ly.layer(8, 0), "metal2" )
|
||||
rmetal2_lbl = l2n.make_text_layer( ly.layer(8, 1), "metal2_lbl" )
|
||||
|
||||
|
||||
rsd = ractive - rpoly
|
||||
|
||||
l2n.register(rsd, "sd")
|
||||
|
|
@ -194,7 +194,7 @@ end;
|
|||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist()
|
||||
|
||||
self.assertEqual(str(l2n.netlist()), """circuit TRANS ($1=$1,$2=$2,$3=$3);
|
||||
|
|
@ -247,7 +247,7 @@ end;
|
|||
rnactive = ractive - rnwell
|
||||
rngate = rnactive & rpoly
|
||||
rnsd = rnactive - rngate
|
||||
|
||||
|
||||
# PMOS transistor device extraction
|
||||
pmos_ex = pya.DeviceExtractorMOS3Transistor("PMOS")
|
||||
l2n.extract_devices(pmos_ex, { "SD": rpsd, "G": rpgate, "P": rpoly })
|
||||
|
|
@ -282,8 +282,8 @@ end;
|
|||
l2n.connect(rpoly, rpoly_lbl) # attaches labels
|
||||
l2n.connect(rmetal1, rmetal1_lbl) # attaches labels
|
||||
l2n.connect(rmetal2, rmetal2_lbl) # attaches labels
|
||||
|
||||
# Perform netlist extraction
|
||||
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist()
|
||||
|
||||
self.assertEqual(str(l2n.netlist()), """circuit RINGO ();
|
||||
|
|
@ -397,8 +397,8 @@ end;
|
|||
# Global connections
|
||||
l2n.connect_global(rptie, "BULK")
|
||||
l2n.connect_global(rbulk, "BULK")
|
||||
|
||||
# Perform netlist extraction
|
||||
|
||||
# Perform netlist extraction
|
||||
l2n.extract_netlist()
|
||||
|
||||
self.assertEqual(str(l2n.netlist()), """circuit RINGO ();
|
||||
|
|
@ -455,7 +455,7 @@ end;
|
|||
def test_13_ReadAndWrite(self):
|
||||
|
||||
ut_testsrc = os.getenv("TESTSRC")
|
||||
ut_testtmp = os.getenv("TESTTMP")
|
||||
ut_testtmp = os.getenv("TESTTMP", "")
|
||||
|
||||
l2n = pya.LayoutToNetlist()
|
||||
|
||||
|
|
@ -725,4 +725,3 @@ if __name__ == '__main__':
|
|||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class DBLayoutVsSchematicTests(unittest.TestCase):
|
|||
lvs.connect(metal2)
|
||||
lvs.connect(ptie)
|
||||
lvs.connect(ntie)
|
||||
|
||||
|
||||
# inter-layer
|
||||
lvs.connect(psd, diff_cont)
|
||||
lvs.connect(nsd, diff_cont)
|
||||
|
|
@ -113,7 +113,7 @@ class DBLayoutVsSchematicTests(unittest.TestCase):
|
|||
lvs.connect(poly, poly_lbl) # attaches labels
|
||||
lvs.connect(metal1, metal1_lbl) # attaches labels
|
||||
lvs.connect(metal2, metal2_lbl) # attaches labels
|
||||
|
||||
|
||||
# global
|
||||
lvs.connect_global(ptie, "BULK")
|
||||
lvs.connect_global(bulk, "BULK")
|
||||
|
|
@ -144,7 +144,7 @@ class DBLayoutVsSchematicTests(unittest.TestCase):
|
|||
def test_3_ReadAndWrite(self):
|
||||
|
||||
ut_testsrc = os.getenv("TESTSRC")
|
||||
ut_testtmp = os.getenv("TESTTMP")
|
||||
ut_testtmp = os.getenv("TESTTMP", "")
|
||||
|
||||
lvs = pya.LayoutVsSchematic()
|
||||
|
||||
|
|
@ -181,4 +181,3 @@ if __name__ == '__main__':
|
|||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
# KLayout Layout Viewer
|
||||
# Copyright (C) 2006-2023 Matthias Koefferlein
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
import pya
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
|
||||
def compare(pb1, pb2):
|
||||
|
||||
if pb1.width() != pb2.width() or pb1.height() != pb2.height():
|
||||
return False
|
||||
|
||||
for x in range(0, pb1.width()):
|
||||
for y in range(0, pb1.height()):
|
||||
if pb1.pixel(x, y) != pb2.pixel(x, y):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class LAYPixelBufferTests(unittest.TestCase):
|
||||
|
||||
def test_1(self):
|
||||
|
||||
pb = pya.PixelBuffer(10, 20)
|
||||
pb.transparent = True
|
||||
pb.fill(0xf0010203)
|
||||
|
||||
png = pb.to_png_data()
|
||||
|
||||
# some range because implementations may differ
|
||||
self.assertGreater(len(png), 20)
|
||||
self.assertLess(len(png), 200)
|
||||
pb_copy = pya.PixelBuffer.from_png_data(png)
|
||||
self.assertEqual(compare(pb, pb_copy), True)
|
||||
|
||||
ut_testtmp = os.getenv("TESTTMP", ".")
|
||||
tmp = os.path.join(ut_testtmp, "tmp.png")
|
||||
|
||||
pb.write_png(tmp)
|
||||
pb_copy = pya.PixelBuffer.read_png(tmp)
|
||||
self.assertEqual(compare(pb, pb_copy), True)
|
||||
|
||||
|
||||
# run unit tests
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(LAYPixelBufferTests)
|
||||
|
||||
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
|
||||
sys.exit(1)
|
||||
|
|
@ -667,12 +667,18 @@ class LAYLayers_TestClass < TestBase
|
|||
p.clear_line_style
|
||||
assert_equal( p.has_line_style?, false )
|
||||
|
||||
assert_equal( p.is_expanded?, false )
|
||||
p.expanded = true
|
||||
assert_equal( p.is_expanded?, true )
|
||||
|
||||
pp = RBA::LayerPropertiesNode::new
|
||||
assert_equal( pp == p, false )
|
||||
assert_equal( pp != p, true )
|
||||
assert_equal( pp.is_expanded?, false )
|
||||
pp = p.dup
|
||||
assert_equal( pp == p, true )
|
||||
assert_equal( pp != p, false )
|
||||
assert_equal( pp.is_expanded?, true )
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue