From 9630bff24086f9b1fcddcb8282a42a7062275c1c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 5 May 2026 23:06:44 +0200 Subject: [PATCH] A contribution to issue #2345 mitigation With this patch, empty layers can be used to place device terminals on and these shapes are visible on those layers. This allows splitting the terminal shapes and used those shapes to connect down to different substrates. The patch turns EmptyLayer into a DeepLayer when used as terminal layer for device extraction. --- src/db/db/dbEdgePairs.cc | 7 + src/db/db/dbEdgePairs.h | 5 + src/db/db/dbEdges.cc | 7 + src/db/db/dbEdges.h | 5 + src/db/db/dbNetlistDeviceExtractor.cc | 6 +- src/db/db/dbRegion.cc | 7 + src/db/db/dbRegion.h | 5 + src/db/db/dbShapeCollection.h | 5 + src/db/db/dbTexts.cc | 7 + src/db/db/dbTexts.h | 5 + src/lvs/unit_tests/lvsSimpleTests.cc | 6 + testdata/lvs/split_substrate.cir | 13 ++ testdata/lvs/split_substrate.gds | Bin 0 -> 3182 bytes testdata/lvs/split_substrate.l2n | 215 ++++++++++++++++++++++++++ testdata/lvs/split_substrate.lvs | 107 +++++++++++++ 15 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 testdata/lvs/split_substrate.cir create mode 100644 testdata/lvs/split_substrate.gds create mode 100644 testdata/lvs/split_substrate.l2n create mode 100644 testdata/lvs/split_substrate.lvs diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index cd43514b8..0b5cc36d3 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -103,6 +103,13 @@ EdgePairs::EdgePairs (DeepShapeStore &dss) mp_delegate = new DeepEdgePairs (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +EdgePairs::convert_to_deep (const db::DeepLayer &layer) +{ + tl_assert (mp_delegate->deep () == 0); + set_delegate (new db::DeepEdgePairs (layer)); +} + void EdgePairs::write (const std::string &fn) const { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 7b2b4ed7f..f75d7d773 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -204,6 +204,11 @@ public: */ explicit EdgePairs (DeepShapeStore &dss); + /** + * @brief Converts the shape collection to a deep one using the specified layer + */ + virtual void convert_to_deep (const db::DeepLayer &layer); + /** * @brief Writes the edge pair collection to a file * diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index 1cad75949..1d6b1a72e 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -114,6 +114,13 @@ Edges::Edges (DeepShapeStore &dss) mp_delegate = new DeepEdges (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Edges::convert_to_deep (const db::DeepLayer &layer) +{ + tl_assert (mp_delegate->deep () == 0); + set_delegate (new db::DeepEdges (layer)); +} + const db::RecursiveShapeIterator & Edges::iter () const { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index ff8007d19..a12d09c58 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -264,6 +264,11 @@ public: */ explicit Edges (DeepShapeStore &dss); + /** + * @brief Converts the shape collection to a deep one using the specified layer + */ + virtual void convert_to_deep (const db::DeepLayer &layer); + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbNetlistDeviceExtractor.cc b/src/db/db/dbNetlistDeviceExtractor.cc index 1eb73b68f..9424007b8 100644 --- a/src/db/db/dbNetlistDeviceExtractor.cc +++ b/src/db/db/dbNetlistDeviceExtractor.cc @@ -130,8 +130,10 @@ void NetlistDeviceExtractor::extract (db::DeepShapeStore &dss, unsigned int layo std::pair alias = dss.layer_for_flat (tl::id_of (l->second->get_delegate ())); if (alias.first) { - // use deep layer alias for a given flat one (if found) - layers.push_back (alias.second.layer ()); + // use deep layer alias for a given flat one (if found) and convert layer to a deep one + db::DeepLayer dl = alias.second; + l->second->convert_to_deep (dl); + layers.push_back (dl.layer ()); } else { throw tl::Exception (tl::sprintf (tl::to_string (tr ("Invalid region passed to input layer '%s' for device extraction (device %s): must be of deep region kind")), ld->name, name ())); } diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index f290b87d3..e4d1e1983 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -132,6 +132,13 @@ Region::Region (DeepShapeStore &dss) mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Region::convert_to_deep (const db::DeepLayer &layer) +{ + tl_assert (mp_delegate->deep () == 0); + set_delegate (new db::DeepRegion (layer)); +} + void Region::write (const std::string &fn) const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 655462a01..3a230fdf1 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -256,6 +256,11 @@ public: */ void write (const std::string &fn) const; + /** + * @brief Converts the shape collection to a deep one using the specified layer + */ + virtual void convert_to_deep (const db::DeepLayer &layer); + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbShapeCollection.h b/src/db/db/dbShapeCollection.h index a03c008b2..7f59715ff 100644 --- a/src/db/db/dbShapeCollection.h +++ b/src/db/db/dbShapeCollection.h @@ -102,6 +102,11 @@ public: virtual ShapeCollectionDelegateBase *get_delegate () const = 0; + /** + * @brief Converts the shape collection to a deep one using the specified layer + */ + virtual void convert_to_deep (const db::DeepLayer &layer) = 0; + /** * @brief Applies a PropertyTranslator * diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 902a5cf2e..47420fd39 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -99,6 +99,13 @@ Texts::Texts (DeepShapeStore &dss) mp_delegate = new DeepTexts (DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Texts::convert_to_deep (const db::DeepLayer &layer) +{ + tl_assert (mp_delegate->deep () == 0); + set_delegate (new db::DeepTexts (layer)); +} + void Texts::write (const std::string &fn) const { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 74441be07..276f9b415 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -208,6 +208,11 @@ public: */ void write (const std::string &fn) const; + /** + * @brief Converts the shape collection to a deep one using the specified layer + */ + virtual void convert_to_deep (const db::DeepLayer &layer); + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 53222d344..e248cee48 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -362,3 +362,9 @@ TEST(63_FlagMissingPorts) run_test (_this, "flag_missing_ports", "flag_missing_ports.gds", false, true, "TOP"); } +// Split substrate - marker and global connection (issue #2345) +TEST(64_SplitSubstrate) +{ + run_test (_this, "split_substrate", "split_substrate.gds", true, false /*no LVS*/); +} + diff --git a/testdata/lvs/split_substrate.cir b/testdata/lvs/split_substrate.cir new file mode 100644 index 000000000..c6971c69e --- /dev/null +++ b/testdata/lvs/split_substrate.cir @@ -0,0 +1,13 @@ +* Extracted by KLayout + +.SUBCKT TOP A Q IOSUB SUBSTRATE +X$1 \$7 \$1 Q IOSUB IOSUB INV +X$2 \$7 A \$1 SUBSTRATE SUBSTRATE INV +.ENDS TOP + +.SUBCKT INV \$2 \$4 \$5 \$1 \$I11 +M$1 \$2 \$4 \$5 \$2 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$1 \$4 \$5 \$I11 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/split_substrate.gds b/testdata/lvs/split_substrate.gds new file mode 100644 index 0000000000000000000000000000000000000000..e33ad4d0b0cd8968b263a8db891a33c5f469d51a GIT binary patch literal 3182 zcmbW3J!lj`6vyB0-RuEodf%i?>##20Fu>G)1|MN34bI()EP zgFl;4eIsyx}X& z{1I=Nzvqrmln*fm`gMGk@s%3ShtB--w*MdbbHLX;(Y#S=yd{4Q_`2tsH%g7SMX=NnK;vV ze<%NU2hoo6vkQ;&KDVvZI5W+c{AZu@o_!_fKgZMZpW8XlnVk3RD>dFe|1sC2nBNCs z^d7fM1#1vfn{7`B<$=^7Cocw2x&k2*y5Bo}ux11lnoz2%(G>7#L@SDSP@RdT( zlp1Hs{0~1r^_#ckgy0OAvt-UdX%(LZSL7KuJx<$+o}?{)KkIxD_08fio)PkMYE|5~ zF*Od_>+AUWZtBsGY zNYJhrQ!^PrdQAxjK>u#?u&J4?HiugzPIDP zkomc} psd, "G" => pgate, "W" => nwell, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer + +soft_connect(diff_cont, psd) +soft_connect(diff_cont, nsd) +soft_connect(diff_cont, ptie) +soft_connect(diff_cont, ntie) +soft_connect(ntie, nwell) +soft_connect(poly_cont, poly) + +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Split bulk and ptie into inside and outside iosub +(bulk_io, bulk_reg) = bulk.andnot(iosub) +(ptie_io, ptie_reg) = ptie.andnot(iosub) + +# connect outside to "SUBSTRATE" globally +connect(bulk, bulk_reg) +connect(ptie, ptie_reg) +connect_global(bulk_reg, "SUBSTRATE") +soft_connect_global(ptie_reg, "SUBSTRATE") + +# connect inside to "IOSUB" via polygons +connect(bulk, bulk_io) +connect(ptie, ptie_io) +connect(bulk_io, iosub) +soft_connect(ptie_io, iosub) +connect_global(iosub, "IOSUB") + +# Netlist section (NOTE: we only check log here) +# for debugging: _make_soft_connection_diodes(true) +netlist + +netlist.simplify + +