Merge branch 'master' into wip

This commit is contained in:
Matthias Koefferlein 2023-03-24 15:56:48 +01:00
commit fd0c60761f
42 changed files with 753 additions and 204 deletions

View File

@ -4,7 +4,8 @@ name: Build Python Wheels
on:
pull_request:
push:
# Running on PRs is enough
# push:
release:
types: [published]
@ -23,6 +24,13 @@ jobs:
- os: "ubuntu-latest"
cibuild: "*musllinux*"
steps:
- name: Free Disk Space (Ubuntu)
if: matrix.os == 'ubuntu-latest'
uses: jlumbroso/free-disk-space@main
with:
android: false
dotnet: false
haskell: false
- uses: hmarr/debug-action@v2
- name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.9.1

View File

@ -1,3 +1,23 @@
0.28.6 (2023-03-16):
* Enhancement: %GITHUB%/issues/1249 Include expanded/collapsed state of layer properties into session
* Bugfix: %GITHUB%/issues/1265 Issues installing klayout with pip on macOS related to libpng
* Enhancement: %GITHUB%/issues/1271 __version__ attribute in Python modules available now
* Bugfix: %GITHUB%/issues/1287 Goto Position (CRTL+G) is not showing the origin (0,0)
* Bugfix: %GITHUB%/issues/1291 Better compatibility of PyQt5 and KLayout (i.e. debugger does not crash)
* Enhancement: %GITHUB%/issues/1294 Persisting layer properties in sessions
* Bugfix: %GITHUB%/issues/1302 Select filter is not applied in partial mode
* Bugfix: %GITHUB%/issues/1304 Spice netlist reader: should read "M" terminals in DGS order
* Bugfix: %GITHUB%/issues/1309 Incomplete fill (polygon rasterizer issue)
* Bugfix: %GITHUB%/issues/1315 Cannot export layers from Marker Browser in viewer mode
* Bugfix/enhancement: some LEF/DEF parser issues solved with the help of a new complete sample case
* Bugfix: Avoid a segfault while editing a ruler. This happens is both a selection and a transient selection is active.
* Enhancement: Some enhancements for image editing (e.g. selection remains after moving handles)
* Enhancement: klayout.db Python module is auto-loaded for providing stream readers
* Enhancement: Spice reader now supports parametric subcircuits
* Enhancement: Build issue fixed for Qt 5.15.2 bindings
* Enhancement: Including Python's matplotlib into Windows binaries
* Bugfix: Reading fillcell-generated inputs again into DRC deck now also works for deep mode
0.28.5 (2023-02-05):
* Bugfix: %GITHUB%/issues/1275 Region.sized after .smooth returns empty region

View File

@ -1,3 +1,10 @@
klayout (0.28.6-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Thu, 16 Mar 2023 21:23:46 +0100
klayout (0.28.5-1) unstable; urgency=low
* New features and bugfixes

View File

@ -1,9 +1,9 @@
Relevant KLayout version: 0.28.4<br>
Relevant KLayout version: 0.28.6<br>
Author: Kazzz-S<br>
Last modified: 2023-02-01<br>
Last modified: 2023-03-19<br>
# 1. Introduction
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.4 or later for different 64-bit macOS, including:
This directory **`macbuild`** contains various files required for building KLayout (http://www.klayout.de/) version 0.28.6 or later for different 64-bit macOS, including:
* Catalina (10.15.7) : the primary development environment
* Big Sur (11.x) : experimental; Apple (M1|M2) chip is not tested since the author does not own an (M1|M2) Mac
* Monterey (12.x) : -- ditto --
@ -73,7 +73,7 @@ You need to have the followings:
```
---------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >>
for building KLayout 0.28.4 or later on different Apple macOS / Mac OSX platforms.
for building KLayout 0.28.6 or later on different Apple macOS / Mac OSX platforms.
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
@ -130,7 +130,7 @@ $ [python] ./build4mac.py
```
---------------------------------------------------------------------------------------------------------
<< Usage of 'build4mac.py' >>
for building KLayout 0.28.4 or later on different Apple macOS / Mac OSX platforms.
for building KLayout 0.28.6 or later on different Apple macOS / Mac OSX platforms.
$ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
@ -236,7 +236,7 @@ $ ./build4mac.py -q qt5macports -r mp32 -p mp39
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl, \*.egg) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
```
$ ./build4mac.py -q qt5macports -r mp32 -p mp39 -Y
@ -263,7 +263,7 @@ $ ./build4mac.py -q qt5brew -r hb32 -p hb39
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl, \*.egg) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
```
$ ./build4mac.py -q qt5brew -r hb32 -p hb39 -Y
@ -316,7 +316,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
2. Confirm successful build (it will take about one hour, depending on your machine spec).
3. Rerun **`build4mac.py`** with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed under **klayout.app/Contents/Buddy/** in this step.<br>
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl, \*.egg) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
If you use `--buildPymod` option in Step-1 and Step-3, the KLayout Python Module (\*.whl) will be built and deployed under **klayout.app/Contents/pymod-dist/**.
```
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
@ -352,8 +352,8 @@ $ cd /where/'build.sh'/exists
$ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Catalina-release-Rmp32Pmp39 -m
```
This command will generate the two files below:<br>
* **`LW-klayout-0.28.4-macOS-Catalina-1-qt5MP-Rmp32Pmp39.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.28.4-macOS-Catalina-1-qt5MP-Rmp32Pmp39.dmg.md5`** ---(2) MD5-value text file
* **`LW-klayout-0.28.6-macOS-Catalina-1-qt5MP-Rmp32Pmp39.dmg`** ---(1) the main DMG file
* **`LW-klayout-0.28.6-macOS-Catalina-1-qt5MP-Rmp32Pmp39.dmg.md5`** ---(2) MD5-value text file
# Known issues
Because we assume some specific versions of non-OS-standard Ruby and Python, updating MacPorts, Homebrew, or Anaconda3 may cause build- and link errors.<br>

View File

@ -47,7 +47,7 @@ def GenerateUsage(platform):
usage = "\n"
usage += "---------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\n"
usage += " for building KLayout 0.28.4 or later on different Apple macOS / Mac OSX platforms.\n"
usage += " for building KLayout 0.28.6 or later on different Apple macOS / Mac OSX platforms.\n"
usage += "\n"
usage += "$ [python] ./build4mac.py\n"
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value\n"
@ -905,19 +905,31 @@ def Build_pymod(parameters):
PymodDistDir = parameters['pymod_dist']
# Using MacPorts
if PymodDistDir[ModulePython] == 'dist-MP3':
addBinPath = "/opt/local/bin"
addIncPath = "/opt/local/include"
addLibPath = "/opt/local/lib"
# Using Homebrew
elif PymodDistDir[ModulePython] == 'dist-HB3':
addIncPath = "%s/include" % DefaultHomebrewRoot # defined in "build4mac_env.py"
addLibPath = "%s/lib" % DefaultHomebrewRoot # defined in "build4mac_env.py"
addBinPath = "%s/bin" % DefaultHomebrewRoot # defined in "build4mac_env.py"
addIncPath = "%s/include" % DefaultHomebrewRoot # -- ditto --
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
elif PymodDistDir[ModulePython] == 'dist-ana3':
addBinPath = "/Applications/anaconda3/bin"
addIncPath = "/Applications/anaconda3/include"
addLibPath = "/Applications/anaconda3/lib"
else:
addBinPath = ""
addIncPath = ""
addLibPath = ""
if not addBinPath == "":
try:
bpath = os.environ['PATH']
except KeyError:
os.environ['PATH'] = addBinPath
else:
os.environ['PATH'] = "%s:%s" % (addBinPath, bpath)
if not addIncPath == "":
try:
cpath = os.environ['CPATH']
@ -937,10 +949,11 @@ def Build_pymod(parameters):
#--------------------------------------------------------------------
# [3] Set different command line parameters for building <pymod>
#--------------------------------------------------------------------
cmd1_args = " setup.py build \\\n"
cmd2_args = " setup.py bdist_wheel \\\n"
cmd3_args = " setup.py bdist_egg \\\n"
cmd4_args = " setup.py clean --all \\\n"
cmd1_args = " -m setup build \\\n"
cmd2_args = " -m setup bdist_wheel \\\n"
deloc_cmd = " delocate-wheel --ignore-missing-dependencies"
cmd3_args = " <wheel file> \\\n"
cmd4_args = " -m setup clean --all \\\n"
#--------------------------------------------------------------------
# [4] Make the consolidated command lines
@ -958,7 +971,7 @@ def Build_pymod(parameters):
command2 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command3 = "time"
command3 += " \\\n %s \\\n" % parameters['python']
command3 += " \\\n %s \\\n" % deloc_cmd
command3 += cmd3_args
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
@ -986,6 +999,7 @@ def Build_pymod(parameters):
print( "<Stage-4>")
print( " ", command4 )
print( "" )
if parameters['check_cmd_only']:
return 0
@ -1011,11 +1025,26 @@ def Build_pymod(parameters):
print( "", file=sys.stderr )
return 1
#---------------------------------------------------------------------------------------------------------
# Copy and relink library dependencies for wheel.
# In this step, the "delocate-wheel" command using the desired Python must be found in the PATH.
# Refer to: https://github.com/Kazzz-S/klayout/issues/49#issuecomment-1432154118
# https://pypi.org/project/delocate/
#---------------------------------------------------------------------------------------------------------
cmd3_args = glob.glob( "dist/*.whl" ) # like ['dist/klayout-0.28.6-cp39-cp39-macosx_12_0_x86_64.whl']
if len(cmd3_args) == 1:
command3 = "time"
command3 += " \\\n %s \\\n" % deloc_cmd
command3 += " %s \\\n" % cmd3_args[0]
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
ret = subprocess.call( command3, shell=True )
else:
ret = 1
if ret != 0:
print( "", file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "!!! <%s>: failed to build <pymod-egg>" % myscript, file=sys.stderr )
print( "!!! <%s>: failed to <delocate-wheel>" % myscript, file=sys.stderr )
print( "-------------------------------------------------------------", file=sys.stderr )
print( "", file=sys.stderr )
return 1

View File

@ -196,7 +196,7 @@ Ruby31MacPorts = { 'exe': '/opt/local/bin/ruby3.1',
# install with 'sudo port install ruby32'
# [Key Type Name] = 'MP32'
Ruby32MacPorts = { 'exe': '/opt/local/bin/ruby3.2',
'inc': '/opt/local/include/ruby-3.2.0',
'inc': '/opt/local/include/ruby-3.2.1',
'lib': '/opt/local/lib/libruby.3.2.dylib'
}

View File

@ -47,6 +47,7 @@ public:
virtual void finish (bool);
void keep_for_healing (const db::Polygon &poly);
void keep_for_healing (const db::Box &box);
private:
size_t *mp_count;
@ -78,6 +79,15 @@ public:
}
}
void operator() (const db::Box &box)
{
if (m_healing && ! box.inside (mp_tile->enlarged (db::Vector (-1, -1)))) {
mp_receiver->keep_for_healing (box);
} else {
m_count += 1;
}
}
size_t count () const
{
return m_count;
@ -110,6 +120,12 @@ HealingCountingReceiver::keep_for_healing (const db::Polygon &poly)
m_for_healing.insert (poly);
}
void
HealingCountingReceiver::keep_for_healing (const db::Box &box)
{
m_for_healing.insert (box);
}
void
HealingCountingReceiver::finish (bool)
{
@ -132,7 +148,9 @@ public:
void finish (bool /*success*/);
void keep_for_healing (const db::Polygon &poly);
void keep_for_healing (const db::Box &box);
void output (const db::Polygon &poly);
void output (const db::Box &poly);
private:
db::Layout *mp_layout;
@ -167,6 +185,23 @@ public:
}
}
void operator() (const db::Box &box)
{
if (m_healing && ! box.inside (mp_tile->enlarged (db::Vector (-1, -1)))) {
if (mp_trans->is_complex ()) {
mp_receiver->keep_for_healing (*mp_trans * db::Polygon (box));
} else {
mp_receiver->keep_for_healing (*mp_trans * box);
}
} else {
if (mp_trans->is_complex ()) {
mp_receiver->output (*mp_trans * db::Polygon (box));
} else {
mp_receiver->output (*mp_trans * box);
}
}
}
private:
const db::Box *mp_tile;
bool m_healing;
@ -211,12 +246,24 @@ HealingTileLayoutOutputReceiver::keep_for_healing (const db::Polygon &poly)
m_for_healing.insert (poly);
}
void
HealingTileLayoutOutputReceiver::keep_for_healing (const db::Box &box)
{
m_for_healing.insert (box);
}
void
HealingTileLayoutOutputReceiver::output (const db::Polygon &poly)
{
mp_cell->shapes (m_layer).insert (poly);
}
void
HealingTileLayoutOutputReceiver::output (const db::Box &box)
{
mp_cell->shapes (m_layer).insert (box);
}
// ---------------------------------------------------------------------
struct ResultDescriptor

View File

@ -99,7 +99,7 @@ TEST(1A_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
@ -141,7 +141,7 @@ TEST(1A_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n"
@ -321,7 +321,7 @@ TEST(2_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
""
);
@ -354,7 +354,7 @@ TEST(2_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
""
);
@ -387,7 +387,7 @@ TEST(3_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -453,7 +453,7 @@ TEST(3_FlatHeal)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -520,7 +520,7 @@ TEST(3_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -553,7 +553,7 @@ TEST(4_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -586,7 +586,7 @@ TEST(4_FlatHeal)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -619,7 +619,7 @@ TEST(4_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -652,7 +652,7 @@ TEST(5_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -685,7 +685,7 @@ TEST(5_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -718,7 +718,7 @@ TEST(6_Flat)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);
@ -751,7 +751,7 @@ TEST(6_Deep)
reader.read (layout);
}
db::compare_layouts (this, layout, au, db::NoNormalization);
db::compare_layouts (this, layout, au, db::NormalizationMode (db::NoNormalization | db::AsPolygons));
EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n"
);

View File

@ -1146,14 +1146,14 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
check.set_min_projection (options.min_projection);
check.set_max_projection (options.max_projection);
std::vector<generic_shape_iterator<db::Polygon> > others;
std::vector<db::RegionIterator> others;
std::vector<bool> foreign;
bool has_other = false;
bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) {
foreign.push_back (other == foreign_regionptr ());
others.push_back (begin_merged ());
others.push_back (polygons);
other_is_merged = primary_is_merged;
} else {
foreign.push_back (false);

View File

@ -376,15 +376,12 @@ private:
while (cc != current) {
rec.finish (cc->first, cc->second);
typename std::set<std::pair<const Obj *, const Obj *> >::iterator s;
s = seen.lower_bound (std::make_pair (cc->first, (const Obj *)0));
auto s = seen.lower_bound (std::make_pair (cc->first, (const Obj *)0));
auto s0 = s;
while (s != seen.end () && s->first == cc->first) {
seen.erase (s++);
}
s = seen.lower_bound (std::make_pair ((const Obj *)0, cc->first));
while (s != seen.end () && s->second == cc->first) {
seen.erase (s++);
++s;
}
seen.erase (s0, s);
++cc;
}
@ -429,8 +426,8 @@ private:
for (iterator_type i = f0; i != f; ++i) {
for (iterator_type j = c; j < i; ++j) {
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
if (seen.insert (std::make_pair (i->first, j->first)).second) {
seen.insert (std::make_pair (j->first, i->first));
if (seen.find (std::make_pair (i->first, j->first)) == seen.end () && seen.find (std::make_pair (j->first, i->first)) == seen.end ()) {
seen.insert (std::make_pair (i->first, j->first));
rec.add (i->first, i->second, j->first, j->second);
if (rec.stop ()) {
return false;
@ -791,21 +788,23 @@ private:
while (cc1 != current1) {
rec.finish1 (cc1->first, cc1->second);
typename std::set<std::pair<const Obj1 *, const Obj2 *> >::iterator s;
s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0));
auto s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0));
auto s0 = s;
while (s != seen1.end () && s->first == cc1->first) {
seen1.erase (s++);
++s;
}
seen1.erase (s0, s);
++cc1;
}
while (cc2 != current2) {
rec.finish2 (cc2->first, cc2->second);
typename std::set<std::pair<const Obj2 *, const Obj1 *> >::iterator s;
s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0));
auto s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0));
auto s0 = s;
while (s != seen2.end () && s->first == cc2->first) {
seen2.erase (s++);
++s;
}
seen2.erase (s0, s);
++cc2;
}
@ -883,7 +882,7 @@ private:
x = xx;
if (m_report_progress) {
progress->set (std::min (f1 - m_pp1.begin (), f2 - m_pp2.begin ()));
progress->set ((f1 - m_pp1.begin ()) + (f2 - m_pp2.begin ()));
}
}

View File

@ -867,7 +867,9 @@ void DeepShapeStore::remove_ref (unsigned int layout, unsigned int layer)
unsigned int
DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
{
layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, trans));
size_t gen_id = si.layout () ? si.layout ()->hier_generation_id () : 0;
layout_map_type::iterator l = m_layout_map.find (std::make_pair (si, std::make_pair (gen_id, trans)));
if (l == m_layout_map.end () || m_layouts[l->second] == 0) {
unsigned int layout_index;
@ -886,7 +888,7 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
m_layout_map[std::make_pair (si, trans)] = layout_index;
m_layout_map[std::make_pair (si, std::make_pair (gen_id, trans))] = layout_index;
return layout_index;
} else {
@ -896,7 +898,8 @@ DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db:
void DeepShapeStore::make_layout (unsigned int layout_index, const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans)
{
tl_assert (m_layout_map.find (std::make_pair (si, trans)) == m_layout_map.end ());
size_t gen_id = si.layout () ? si.layout ()->hier_generation_id () : 0;
tl_assert (m_layout_map.find (std::make_pair (si, std::make_pair (gen_id, trans))) == m_layout_map.end ());
while (m_layouts.size () <= layout_index) {
m_layouts.push_back (0);
@ -909,7 +912,7 @@ void DeepShapeStore::make_layout (unsigned int layout_index, const db::Recursive
layout.dbu (si.layout ()->dbu () / trans.mag ());
}
m_layout_map[std::make_pair (si, trans)] = layout_index;
m_layout_map[std::make_pair (si, std::make_pair (gen_id, trans))] = layout_index;
}
DeepLayer DeepShapeStore::create_polygon_layer (const db::RecursiveShapeIterator &si, double max_area_ratio, size_t max_vertex_count, const db::ICplxTrans &trans)

View File

@ -298,7 +298,7 @@ private:
struct DB_PUBLIC RecursiveShapeIteratorCompareForTargetHierarchy
{
bool operator () (const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> &a, const std::pair<db::RecursiveShapeIterator, db::ICplxTrans> &b) const
bool operator () (const std::pair<db::RecursiveShapeIterator, std::pair<size_t, db::ICplxTrans> > &a, const std::pair<db::RecursiveShapeIterator, std::pair<size_t, db::ICplxTrans> > &b) const
{
int cmp_iter = db::compare_iterators_with_respect_to_target_hierarchy (a.first, b.first);
if (cmp_iter != 0) {
@ -865,7 +865,7 @@ private:
void issue_variants (unsigned int layout, const std::map<db::cell_index_type, std::map<db::ICplxTrans, db::cell_index_type> > &var_map);
typedef std::map<std::pair<db::RecursiveShapeIterator, db::ICplxTrans>, unsigned int, RecursiveShapeIteratorCompareForTargetHierarchy> layout_map_type;
typedef std::map<std::pair<db::RecursiveShapeIterator, std::pair<size_t, db::ICplxTrans> >, unsigned int, RecursiveShapeIteratorCompareForTargetHierarchy> layout_map_type;
// no copying
DeepShapeStore (const DeepShapeStore &);

View File

@ -146,7 +146,7 @@ public:
* @brief Creates an empty device parameter definition
*/
DeviceParameterDefinition ()
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0)
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0), m_geo_scaling (0.0)
{
// .. nothing yet ..
}
@ -154,8 +154,8 @@ public:
/**
* @brief Creates a device parameter definition with the given name and description
*/
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling)
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0, double geo_scaling = 0.0)
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling), m_geo_scaling (geo_scaling)
{
// .. nothing yet ..
}
@ -192,18 +192,10 @@ public:
m_description = d;
}
/**
* @brief Gets the parameter default value
*/
double default_value () const
{
return m_default_value;
}
/**
* @brief Gets the SI unit scaling factor
*
* Some parameters are given in micrometers for example. This
* Some parameters are given in micrometers - for example W and L of MOS devices. This
* scaling factor gives the translation to SI units (1e-6 for micrometers).
*/
double si_scaling () const
@ -212,7 +204,43 @@ public:
}
/**
* @brief Sets the parameter description
* @brief Set the SI unit scaling factor
*/
void set_si_scaling (double s)
{
m_si_scaling = s;
}
/**
* @brief Gets the geometry scaling exponent
*
* The geometry scaling exponent is used for example when applying .option scale
* in Spice reading. It is 0 for "no scaling", 1 for linear scaling and 2 for
* quadratic scaling.
*/
double geo_scaling_exponent () const
{
return m_geo_scaling;
}
/**
* @brief Sets the geometry scaling exponent
*/
void set_geo_scaling_exponent (double e)
{
m_geo_scaling = e;
}
/**
* @brief Gets the parameter default value
*/
double default_value () const
{
return m_default_value;
}
/**
* @brief Sets the parameter default value
*/
void set_default_value (double d)
{
@ -267,6 +295,7 @@ private:
size_t m_id;
bool m_is_primary;
double m_si_scaling;
double m_geo_scaling;
void set_id (size_t id)
{

View File

@ -487,10 +487,10 @@ DeviceClassResistor::DeviceClassResistor ()
equivalent_terminal_id (terminal_id_A, terminal_id_B);
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
}
// ------------------------------------------------------------------------------------
@ -526,8 +526,8 @@ DeviceClassCapacitor::DeviceClassCapacitor ()
equivalent_terminal_id (terminal_id_A, terminal_id_B);
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
}
// ------------------------------------------------------------------------------------
@ -580,8 +580,8 @@ DeviceClassDiode::DeviceClassDiode ()
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode"));
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode"));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
}
// ------------------------------------------------------------------------------------
@ -608,12 +608,12 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
equivalent_terminal_id (terminal_id_D, terminal_id_S);
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
}
bool
@ -886,12 +886,12 @@ DeviceClassBJT3Transistor::DeviceClassBJT3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter"));
// NOTE: the emitter area and the emitter count are the primary parameters
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12));
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6));
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("NE", "Emitter count", 1.0, true));
}

View File

@ -399,6 +399,7 @@ private:
void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets);
void read_circuit (tl::Extractor &ex, const std::string &name);
bool read_card ();
void read_options (tl::Extractor &ex);
void ensure_circuit ();
std::string get_line ();
void error (const std::string &msg);
@ -625,6 +626,10 @@ SpiceCircuitDict::read_card ()
std::string nc = read_name (ex, mp_netlist);
read_circuit (ex, nc);
} else if (ex.test_without_case (".options")) {
read_options (ex);
} else if (ex.test_without_case (".ends")) {
return true;
@ -691,6 +696,56 @@ SpiceCircuitDict::read_card ()
return false;
}
void
SpiceCircuitDict::read_options (tl::Extractor &ex)
{
while (! ex.at_end ()) {
std::string n;
ex.read_word_or_quoted (n, allowed_name_chars);
n = tl::to_lower_case (n);
double v = 0.0;
std::string w;
if (ex.test ("=")) {
if (ex.try_read (v)) {
// take value
} else {
// skip until end or next space
ex.skip ();
while (! ex.at_end () && ! isspace (*ex)) {
++ex;
}
}
}
// TODO: further options?
const double min_value = 1e-18;
if (n == "scale") {
if (v > min_value) {
mp_delegate->options ().scale = v;
}
} else if (n == "defad") {
if (v > min_value) {
mp_delegate->options ().defad = v;
}
} else if (n == "defas") {
if (v > min_value) {
mp_delegate->options ().defas = v;
}
} else if (n == "defl") {
if (v > min_value) {
mp_delegate->options ().defl = v;
}
} else if (n == "defw") {
if (v > min_value) {
mp_delegate->options ().defw = v;
}
}
}
}
void
SpiceCircuitDict::ensure_circuit ()
{
@ -1162,9 +1217,9 @@ SpiceNetlistBuilder::build_global_nets ()
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_delegate (delegate), m_strict (false)
{
static NetlistSpiceReaderDelegate std_delegate;
if (! delegate) {
mp_delegate.reset (&std_delegate);
mp_default_delegate.reset (new NetlistSpiceReaderDelegate ());
mp_delegate.reset (mp_default_delegate.get ());
}
}

View File

@ -32,6 +32,7 @@
#include <map>
#include <string>
#include <memory>
namespace db
{
@ -64,6 +65,7 @@ public:
private:
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::unique_ptr<NetlistSpiceReaderDelegate> mp_default_delegate;
bool m_strict;
};

View File

@ -86,8 +86,20 @@ static std::string unescape_name (const std::string &n)
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderOptions::NetlistSpiceReaderOptions ()
{
scale = 1.0;
defad = 0.0;
defas = 0.0;
// ngspice defaults:
defw = 100e-6;
defl = 100e-6;
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
: mp_netlist (0)
: mp_netlist (0), m_options ()
{
// .. nothing yet ..
}
@ -97,6 +109,22 @@ NetlistSpiceReaderDelegate::~NetlistSpiceReaderDelegate ()
// .. nothing yet ..
}
void NetlistSpiceReaderDelegate::set_netlist (db::Netlist *netlist)
{
m_options = NetlistSpiceReaderOptions ();
mp_netlist = netlist;
}
void NetlistSpiceReaderDelegate::do_start ()
{
start (mp_netlist);
}
void NetlistSpiceReaderDelegate::do_finish ()
{
finish (mp_netlist);
}
void NetlistSpiceReaderDelegate::start (db::Netlist * /*netlist*/)
{
// .. nothing yet ..
@ -196,7 +224,8 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
if (ex.try_read_word (n) && ex.test ("=")) {
// a parameter
pv [mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n)] = read_value (ex, variables);
std::string pn = mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n);
pv [pn] = read_value (ex, variables);
} else {
@ -231,8 +260,21 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
}
}
void NetlistSpiceReaderDelegate::def_values_per_element (const std::string &element, std::map<std::string, tl::Variant> &pv)
{
if (element == "M") {
pv.insert (std::make_pair ("W", m_options.defw));
pv.insert (std::make_pair ("L", m_options.defl));
pv.insert (std::make_pair ("AD", m_options.defad));
pv.insert (std::make_pair ("AS", m_options.defas));
}
}
void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables)
{
def_values_per_element (element, pv);
parse_element_components (s, nn, pv, variables);
// interpret the parameters according to the code
@ -337,6 +379,8 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
std::map<std::string, tl::Variant> params = pv;
std::vector<size_t> terminal_order;
size_t defp = std::numeric_limits<size_t>::max ();
double mult = 1.0;
auto mp = params.find ("M");
if (mp != params.end ()) {
@ -378,9 +422,20 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
error (tl::to_string (tr ("A 'R' element requires two or three nets")));
}
// Apply multiplier
// Apply multiplier (divider, according to ngspice manual)
value /= mult;
defp = db::DeviceClassResistor::param_id_R;
// Apply multiplier to other parameters
static const char *scale_params[] = { "A", "P", "W" };
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
}
} else if (element == "L") {
if (nets.size () == 2) {
@ -398,9 +453,11 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
error (tl::to_string (tr ("A 'L' element requires two nets")));
}
// Apply multiplier
// Apply multiplier (divider, according to ngspice manual)
value /= mult;
defp = db::DeviceClassInductor::param_id_L;
} else if (element == "C") {
if (nets.size () == 2) {
@ -432,6 +489,17 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// Apply multiplier
value *= mult;
defp = db::DeviceClassCapacitor::param_id_C;
// Apply multiplier to other parameters
static const char *scale_params[] = { "A", "P" };
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
}
} else if (element == "D") {
if (cls) {
@ -445,11 +513,14 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
}
// Apply multiplier to "A"
auto p = params.find ("A");
// Apply multiplier
static const char *scale_params[] = { "A", "P" };
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
}
} else if (element == "Q") {
@ -479,11 +550,14 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
}
}
// Apply multiplier to "AE"
auto p = params.find ("AE");
// Apply multiplier
static const char *scale_params[] = { "AE", "PE", "AB", "PB", "AC", "PC" };
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
}
} else if (element == "M") {
@ -502,11 +576,14 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
}
}
// Apply multiplier to "W"
auto p = params.find ("W");
// Apply multiplier
static const char *scale_params[] = { "W", "AD", "AS", "PD", "PS" };
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
}
// issue #1304
terminal_order.push_back (DeviceClassMOS4Transistor::terminal_id_D);
@ -537,28 +614,38 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
}
size_t defp = std::numeric_limits<size_t>::max ();
if (dynamic_cast<db::DeviceClassCapacitor *> (cls)) {
defp = db::DeviceClassCapacitor::param_id_C;
} else if (dynamic_cast<db::DeviceClassResistor *> (cls)) {
defp = db::DeviceClassResistor::param_id_R;
} else if (dynamic_cast<db::DeviceClassInductor *> (cls)) {
defp = db::DeviceClassInductor::param_id_L;
}
std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions_non_const ();
for (std::vector<db::DeviceParameterDefinition>::iterator i = pd.begin (); i != pd.end (); ++i) {
auto v = params.find (i->name ());
double pv = 0.0;
if (v != params.end ()) {
device->set_parameter_value (i->id (), v->second.to_double () / i->si_scaling ());
pv = v->second.to_double ();
} else if (i->id () == defp) {
device->set_parameter_value (i->id (), value / i->si_scaling ());
pv = value;
} else {
continue;
}
device->set_parameter_value (i->id (), pv);
}
apply_parameter_scaling (device);
return true;
}
void
NetlistSpiceReaderDelegate::apply_parameter_scaling (db::Device *device) const
{
if (! device || ! device->device_class ()) {
return;
}
const std::vector<db::DeviceParameterDefinition> &pd = device->device_class ()->parameter_definitions ();
for (auto i = pd.begin (); i != pd.end (); ++i) {
double pv = device->parameter_value (i->id ());
device->set_parameter_value (i->id (), pv / i->si_scaling () * pow (m_options.scale, i->geo_scaling_exponent ()));
}
}
tl::Variant
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables)
{

View File

@ -40,6 +40,14 @@ class Circuit;
class DeviceClass;
class Device;
struct DB_PUBLIC NetlistSpiceReaderOptions
{
NetlistSpiceReaderOptions ();
double scale;
double defad, defas, defw, defl;
};
/**
* @brief A delegate to handle various forms of devices and translates them
*
@ -55,6 +63,22 @@ public:
NetlistSpiceReaderDelegate ();
virtual ~NetlistSpiceReaderDelegate ();
/**
* @brief Gets the reader options
*/
const NetlistSpiceReaderOptions &options () const
{
return m_options;
}
/**
* @brief Gets the reader options (non-const)
*/
NetlistSpiceReaderOptions &options ()
{
return m_options;
}
/**
* @brief Called when the netlist reading starts
*/
@ -122,7 +146,6 @@ public:
/**
* @brief Reads a set of string components and parameters from the string
* A special key "param:" is recognized for starting a parameter list.
*/
void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables);
@ -139,29 +162,28 @@ public:
/**
* @brief External interface for start
*/
void do_start ()
{
start (mp_netlist);
}
void do_start ();
/**
* @brief External interface for finish
*/
void do_finish ()
{
finish (mp_netlist);
}
void do_finish ();
/**
* @brief Sets the netlist
*/
void set_netlist (db::Netlist *netlist)
{
mp_netlist = netlist;
}
void set_netlist (db::Netlist *netlist);
/**
* @brief Applies SI and geometry scaling to the device parameters
*/
void apply_parameter_scaling (db::Device *device) const;
private:
db::Netlist *mp_netlist;
NetlistSpiceReaderOptions m_options;
void def_values_per_element (const std::string &element, std::map<std::string, tl::Variant> &pv);
};
}

View File

@ -44,7 +44,8 @@ static bool to_bool (const tl::Variant &v)
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars)
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale)
: m_def_scale (def_scale)
{
static variables_type empty_variables;
mp_variables = vars ? vars : &empty_variables;
@ -202,7 +203,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
*status = true;
}
double f = 1.0;
double f = m_def_scale;
if (*ex == 't' || *ex == 'T') {
f = 1e12;
} else if (*ex == 'g' || *ex == 'G') {

View File

@ -45,7 +45,7 @@ class DB_PUBLIC NetlistSpiceReaderExpressionParser
public:
typedef std::map<std::string, tl::Variant> variables_type;
NetlistSpiceReaderExpressionParser (const variables_type *vars);
NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale = 1.0);
tl::Variant read (tl::Extractor &ex) const;
tl::Variant read (const std::string &s) const;
@ -54,6 +54,7 @@ public:
private:
const variables_type *mp_variables;
double m_def_scale;
tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const;
tl::Variant read_dot_expr (tl::Extractor &ex, bool *status) const;

View File

@ -1685,6 +1685,20 @@ AreaMap::bbox () const
// -------------------------------------------------------------------------
// Implementation of rasterize
static bool edge_is_partially_left_of (const db::Edge &e, const db::Edge &e_original, db::Coord x)
{
Coord xmin = db::edge_xmin (e);
if (xmin < x) {
return true;
} else if (xmin == x && e_original.dx () != 0) {
// the skew edge is cut partially rendering a straight vertical line (due to rounding)
// which we will count as "left of"
return true;
} else {
return false;
}
}
bool
rasterize (const db::Polygon &polygon, db::AreaMap &am)
{
@ -1801,7 +1815,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (left);
if (ec.first && db::edge_xmin (ec.second) < x) {
if (ec.first && edge_is_partially_left_of (ec.second, *e, x)) {
a += area_type (ec.second.dy ()) * area_type (px);
}
@ -1818,7 +1832,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell);
if (ec.first && db::edge_xmin (ec.second) < xx) {
if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) {
aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2;
a += area_type (ec.second.dy ()) * area_type (px);
}
@ -1832,7 +1846,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell);
if (ec.first && db::edge_xmin (ec.second) < xx) {
if (ec.first && edge_is_partially_left_of (ec.second, *e, xx)) {
aa += (area_type (ec.second.dy ()) * area_type (2 * xx - (ec.second.p2 ().x () + ec.second.p1 ().x ()))) / 2;
}
@ -1843,7 +1857,7 @@ rasterize (const db::Polygon &polygon, db::AreaMap &am)
for (std::vector <db::Edge>::iterator e = cc; e != fff; ++e) {
std::pair<bool, db::Edge> wide_ec = e->clipped (wide_cell);
if (wide_ec.first && db::edge_xmin (wide_ec.second) < x + dx) {
if (wide_ec.first && edge_is_partially_left_of (wide_ec.second, *e, x + dx)) {
a += area_type (wide_ec.second.dy ()) * area_type (px);
}

View File

@ -52,6 +52,31 @@ public:
mp_shapes->insert (t.transformed (m_trans));
}
template <class P>
void insert_polygon (const P &p)
{
if (p.is_box () && ! m_trans.is_complex ()) {
mp_shapes->insert (p.box ().transformed (m_trans));
} else {
if (mp_shapes->cell () && mp_shapes->cell ()->layout ()) {
db::polygon_ref<P, db::Disp> pr (p.transformed (m_trans), mp_shapes->cell ()->layout ()->shape_repository ());
mp_shapes->insert (pr);
} else {
mp_shapes->insert (p.transformed (m_trans));
}
}
}
void operator() (const db::Polygon &p)
{
insert_polygon (p);
}
void operator() (const db::SimplePolygon &p)
{
insert_polygon (p);
}
void operator() (const db::EdgePair &ep)
{
mp_shapes->insert (ep.normalized ().to_polygon (m_ep_sizing).transformed (m_trans));

View File

@ -135,6 +135,27 @@ private:
TilingProcessor *mp_proc;
};
/**
* @brief Delivery of tiling processor output
* This utility is put between the container and the receiver.
* The inserter is an object having an operator() that takes the object.
* This function is responsible for preparing (i.e. clipping) and delivering the output.
*/
template <class X>
void insert (X &inserter, const db::Box &o, const db::Box &tile, bool clip)
{
if (clip) {
// clipping
db::Box oc = o & tile;
if (! oc.empty () && oc.width () > 0 && oc.height () > 0) {
inserter (oc);
}
} else {
// no clipping
inserter (o);
}
}
/**
* @brief Delivery of tiling processor output
* This utility is put between the container and the receiver.
@ -144,7 +165,10 @@ private:
template <class X>
void insert (X &inserter, const db::Polygon &o, const db::Box &tile, bool clip)
{
if (clip && ! o.box ().inside (tile)) {
if (o.is_box ()) {
// simple case: box
insert (inserter, o.box (), tile, clip);
} else if (clip && ! o.box ().inside (tile)) {
// apply clipping
if (o.box ().touches (tile)) {
std::vector <db::Polygon> clipped_poly;
@ -168,7 +192,10 @@ void insert (X &inserter, const db::Polygon &o, const db::Box &tile, bool clip)
template <class X>
void insert (X &inserter, const db::SimplePolygon &o, const db::Box &tile, bool clip)
{
if (clip && ! o.box ().inside (tile)) {
if (o.is_box ()) {
// simple case: box
insert (inserter, o.box (), tile, clip);
} else if (clip && ! o.box ().inside (tile)) {
// apply clipping
if (o.box ().touches (tile)) {
std::vector <db::SimplePolygon> clipped_poly;
@ -242,27 +269,6 @@ void insert (X &inserter, const db::Text &o, const db::Box &tile, bool clip)
}
}
/**
* @brief Delivery of tiling processor output
* This utility is put between the container and the receiver.
* The inserter is an object having an operator() that takes the object.
* This function is responsible for preparing (i.e. clipping) and delivering the output.
*/
template <class X>
void insert (X &inserter, const db::Box &o, const db::Box &tile, bool clip)
{
if (clip) {
// clipping
db::Box oc = o & tile;
if (! oc.empty ()) {
inserter (oc);
}
} else {
// no clipping
inserter (o);
}
}
/**
* @brief Delivery of tiling processor output
* This utility is put between the container and the receiver.

View File

@ -790,19 +790,20 @@ Class<db::DeviceTerminalDefinition> decl_dbDeviceTerminalDefinition ("db", "Devi
"This class has been added in version 0.26."
);
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling)
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling, double geo_scaling_exponent)
{
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling);
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling, geo_scaling_exponent);
}
Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition",
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0),
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0), gsi::arg ("geo_scaling_exponent", 0.0),
"@brief Creates a new parameter definition.\n"
"@param name The name of the parameter\n"
"@param description The human-readable description\n"
"@param default_value The initial value\n"
"@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n"
"@param si_scaling The scaling factor to SI units\n"
"@param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic)\n"
) +
gsi::method ("name", &db::DeviceParameterDefinition::name,
"@brief Gets the name of the parameter."
@ -834,7 +835,26 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
) +
gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling,
"@brief Gets the scaling factor to SI units.\n"
"For parameters in micrometers for example, this factor will be 1e-6."
"For parameters in micrometers - for example W and L of MOS devices - this factor can be set to 1e-6 to reflect "
"the unit."
) +
gsi::method ("si_scaling=", &db::DeviceParameterDefinition::set_si_scaling,
"@brief Sets the scaling factor to SI units.\n"
"\n"
"This setter has been added in version 0.28.6."
) +
gsi::method ("geo_scaling_exponent", &db::DeviceParameterDefinition::geo_scaling_exponent,
"@brief Gets the geometry scaling exponent.\n"
"This value is used when applying '.options scale' in the SPICE reader for example. "
"It is zero for 'no scaling', 1.0 for linear scaling and 2.0 for quadratic scaling.\n"
"\n"
"This attribute has been added in version 0.28.6."
) +
gsi::method ("geo_scaling_exponent=", &db::DeviceParameterDefinition::set_geo_scaling_exponent,
"@brief Sets the geometry scaling exponent.\n"
"See \\geo_scaling_exponent for details.\n"
"\n"
"This attribute has been added in version 0.28.6."
) +
gsi::method ("id", &db::DeviceParameterDefinition::id,
"@brief Gets the ID of the parameter.\n"
@ -2749,6 +2769,16 @@ Class<ParseElementData> db_ParseElementData ("db", "ParseElementData",
"This helper class has been introduced in version 0.27.1. Starting with version 0.28.6, named parameters can be string types too.\n"
);
static double get_delegate_scale (const db::NetlistSpiceReaderDelegate *delegate)
{
return delegate->options ().scale;
}
static void apply_parameter_scaling (const db::NetlistSpiceReaderDelegate *delegate, db::Device *device)
{
delegate->apply_parameter_scaling (device);
}
Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate",
gsi::method_ext ("start", &start_fb, "@hide") +
gsi::method_ext ("finish", &finish_fb, "@hide") +
@ -2826,6 +2856,21 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
"@brief Issues an error with the given message.\n"
"Use this method to generate an error."
) +
gsi::method_ext ("get_scale", &get_delegate_scale,
"@brief Gets the scale factor set with '.options scale=...'\n"
"This method has been introduced in version 0.28.6."
) +
gsi::method_ext ("apply_parameter_scaling", &apply_parameter_scaling, gsi::arg ("device"),
"@brief Applies parameter scaling to the given device\n"
"Applies SI scaling (according to the parameter's si_scaling attribute) and "
"geometry scaling (according to the parameter's geo_scale_exponent attribute) to "
"the device parameters. Use this method of finish the device when you have created "
"a custom device yourself.\n"
"\n"
"The geometry scale is taken from the '.options scale=...' control statement.\n"
"\n"
"This method has been introduced in version 0.28.6."
) +
gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"), gsi::arg ("variables", db::NetlistSpiceReader::parameters_type (), "{}"),
"@brief Translates a string into a value\n"
"This function simplifies the implementation of SPICE readers by providing a translation of a unit-annotated string "

View File

@ -313,3 +313,35 @@ TEST(4c)
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au4c.gds");
}
// issue #1309
TEST(5)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool5.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}
db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));
db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));
db::Region remaining_polygons;
db::Vector rs (50, 0);
db::Vector cs (0, 50);
db::Box fc_box (db::Point (), db::Point (rs.x (), cs.y ()));
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), false, &remaining_polygons);
unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0));
remaining_polygons.insert_into (&ly, top_cell, l100);
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au5.oas", db::WriteOAS);
}

View File

@ -704,16 +704,50 @@ TEST(18_XSchemOutput)
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
"end;\n"
"circuit 'PMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=1.305,AD=0.87,PS=10.74,PD=7.16);\n"
"end;\n"
"circuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=1.305,AD=0.87,PS=10.74,PD=7.16);\n"
"end;\n"
"circuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.87,AD=0.435,PS=7.16,PD=3.58);\n"
"end;\n"
"circuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.87,AD=0.435,PS=7.16,PD=3.58);\n"
"end;\n"
);
}
TEST(19_ngspice_ref)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit .TOP ();\n"
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=4,W=1.5)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=4,W=1.5)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
"end;\n"
"circuit 'PMOS4_STANDARD(L=0.15,NF=4,W=1.5)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n"
"end;\n"
"circuit 'NMOS4_STANDARD(L=0.15,NF=4,W=1.5)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n"
"end;\n"
"circuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n"
"end;\n"
"circuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' (D=D,G=G,S=S,B=B);\n"
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n"
"end;\n"
);
}
@ -828,3 +862,18 @@ TEST(100_ExpressionParser)
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true);
EXPECT_EQ (v.to_string (), "6");
}
TEST(101_ExpressionParserWithDefScale)
{
std::map<std::string, tl::Variant> vars;
vars["A"] = 17.5;
tl::Variant v;
db::NetlistSpiceReaderExpressionParser parser (&vars, 1e-3);
EXPECT_EQ (parser.read ("1.75").to_string (), "0.00175");
EXPECT_EQ (parser.read ("-1.75u").to_string (), "-1.75e-06");
EXPECT_EQ (parser.read ("1.75k").to_string (), "1750");
EXPECT_EQ (parser.read ("2*A").to_string (), "0.035");
}

View File

@ -150,8 +150,8 @@ TEST(2)
tp.queue ("_tile && _output(o3, _tile, false)");
tp.execute ("test");
EXPECT_EQ (to_s (ly, top, o1), "polygon (60,10;60,20;70,20;70,10);polygon (10,10;10,30;30,30;30,10)");
EXPECT_EQ (to_s (ly, top, o2), "polygon (50,40;50,70;80,70;80,40)");
EXPECT_EQ (to_s (ly, top, o1), "box (60,10;70,20);box (10,10;30,30)");
EXPECT_EQ (to_s (ly, top, o2), "box (50,40;80,70)");
EXPECT_EQ (to_s (ly, top, o3), "");
ly.clear_layer (o1);
@ -164,9 +164,9 @@ TEST(2)
tp.execute ("test");
EXPECT_EQ (to_s (ly, top, o1), "polygon (10,10;10,23;20,23;20,10);polygon (10,23;10,30;20,30;20,23);polygon (20,10;20,23;30,23;30,10);polygon (20,23;20,30;30,30;30,23);polygon (60,10;60,20;70,20;70,10)");
EXPECT_EQ (to_s (ly, top, o1), "box (10,10;20,23);box (10,23;20,30);box (20,10;30,23);box (20,23;30,30);box (60,10;70,20)");
EXPECT_EQ (to_s (ly, top, o2), "");
EXPECT_EQ (to_s (ly, top, o3), "polygon (-5,-2;-5,23;20,23;20,-2);polygon (-5,23;-5,48;20,48;20,23);polygon (-5,48;-5,73;20,73;20,48);polygon (20,-2;20,23;45,23;45,-2);polygon (20,23;20,48;45,48;45,23);polygon (20,48;20,73;45,73;45,48);polygon (45,-2;45,23;70,23;70,-2);polygon (45,23;45,48;70,48;70,23);polygon (45,48;45,73;70,73;70,48);polygon (70,-2;70,23;95,23;95,-2);polygon (70,23;70,48;95,48;95,23);polygon (70,48;70,73;95,73;95,48);polygon (95,-2;95,23;120,23;120,-2);polygon (95,23;95,48;120,48;120,23);polygon (95,48;95,73;120,73;120,48);polygon (120,-2;120,23;145,23;145,-2);polygon (120,23;120,48;145,48;145,23);polygon (120,48;120,73;145,73;145,48)");
EXPECT_EQ (to_s (ly, top, o3), "box (-5,-2;20,23);box (-5,23;20,48);box (-5,48;20,73);box (20,-2;45,23);box (20,23;45,48);box (20,48;45,73);box (45,-2;70,23);box (45,23;70,48);box (45,48;70,73);box (70,-2;95,23);box (70,23;95,48);box (70,48;95,73);box (95,-2;120,23);box (95,23;120,48);box (95,48;120,73);box (120,-2;145,23);box (120,23;145,48);box (120,48;145,73)");
}
{
@ -185,8 +185,8 @@ TEST(2)
tp.queue ("_output(o3, _tile)");
tp.execute ("test");
EXPECT_EQ (to_s (ly, top, o1), "polygon (60,10;60,20;70,20;70,10);polygon (10,10;10,30;30,30;30,10)");
EXPECT_EQ (to_s (ly, top, o2), "polygon (50,40;50,70;80,70;80,40)");
EXPECT_EQ (to_s (ly, top, o1), "box (60,10;70,20);box (10,10;30,30)");
EXPECT_EQ (to_s (ly, top, o2), "box (50,40;80,70)");
EXPECT_EQ (to_s (ly, top, o3), "");
ly.clear_layer (o1);
@ -199,9 +199,9 @@ TEST(2)
tp.execute ("test");
EXPECT_EQ (to_s (ly, top, o1), "polygon (10,10;10,23;20,23;20,10);polygon (10,23;10,30;20,30;20,23);polygon (20,10;20,23;30,23;30,10);polygon (20,23;20,30;30,30;30,23);polygon (60,10;60,20;70,20;70,10)");
EXPECT_EQ (to_s (ly, top, o1), "box (10,10;20,23);box (10,23;20,30);box (20,10;30,23);box (20,23;30,30);box (60,10;70,20)");
EXPECT_EQ (to_s (ly, top, o2), "");
EXPECT_EQ (to_s (ly, top, o3), "polygon (-5,-2;-5,23;20,23;20,-2);polygon (-5,23;-5,48;20,48;20,23);polygon (-5,48;-5,73;20,73;20,48);polygon (20,-2;20,23;45,23;45,-2);polygon (20,23;20,48;45,48;45,23);polygon (20,48;20,73;45,73;45,48);polygon (45,-2;45,23;70,23;70,-2);polygon (45,23;45,48;70,48;70,23);polygon (45,48;45,73;70,73;70,48);polygon (70,-2;70,23;95,23;95,-2);polygon (70,23;70,48;95,48;95,23);polygon (70,48;70,73;95,73;95,48);polygon (95,-2;95,23;120,23;120,-2);polygon (95,23;95,48;120,48;120,23);polygon (95,48;95,73;120,73;120,48);polygon (120,-2;120,23;145,23;145,-2);polygon (120,23;120,48;145,48;145,23);polygon (120,48;120,73;145,73;145,48)");
EXPECT_EQ (to_s (ly, top, o3), "box (-5,-2;20,23);box (-5,23;20,48);box (-5,48;20,73);box (20,-2;45,23);box (20,23;45,48);box (20,48;45,73);box (45,-2;70,23);box (45,23;70,48);box (45,48;70,73);box (70,-2;95,23);box (70,23;95,48);box (70,48;95,73);box (95,-2;120,23);box (95,23;120,48);box (95,48;120,73);box (120,-2;145,23);box (120,23;145,48);box (120,48;145,73)");
}
}
@ -348,8 +348,8 @@ TEST(4)
tp.execute ("test");
EXPECT_EQ (to_s (o, topo, l1o), "");
EXPECT_EQ (to_s (o, topo, l2o), "polygon (1000,2000;1000,4000;3000,4000;3000,2000)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4000;3000,4000;3000,2000)");
EXPECT_EQ (to_s (o, topo, l2o), "box (1000,2000;3000,4000)");
EXPECT_EQ (to_s (o, topo, l3o), "box (1000,2000;3000,4000)");
o.clear_layer (l1o);
o.clear_layer (l2o);
@ -359,8 +359,8 @@ TEST(4)
tp.execute ("test");
EXPECT_EQ (to_s (o, topo, l1o), "polygon (3000,2000;3000,4000;1000,4000;1000,4010;3010,4010;3010,2000)");
EXPECT_EQ (to_s (o, topo, l2o), "polygon (1000,2000;1000,4000;3000,4000;3000,2000)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4010;3010,4010;3010,2000)");
EXPECT_EQ (to_s (o, topo, l2o), "box (1000,2000;3000,4000)");
EXPECT_EQ (to_s (o, topo, l3o), "box (1000,2000;3010,4010)");
o.clear_layer (l1o);
o.clear_layer (l2o);
@ -370,7 +370,7 @@ TEST(4)
tp.execute ("test");
EXPECT_EQ (to_s (o, topo, l1o), "polygon (1000,4000;1000,4010;1510,4010;1510,4000);polygon (1510,4000;1510,4010;2510,4010;2510,4000);polygon (3000,2000;3000,2510;3010,2510;3010,2000);polygon (3000,2510;3000,3510;3010,3510;3010,2510);polygon (3000,3510;3000,4000;2510,4000;2510,4010;3010,4010;3010,3510)");
EXPECT_EQ (to_s (o, topo, l1o), "polygon (3000,3510;3000,4000;2510,4000;2510,4010;3010,4010;3010,3510);box (1000,4000;1510,4010);box (1510,4000;2510,4010);box (3000,2000;3010,2510);box (3000,2510;3010,3510)");
o.clear_layer (l1o);
o.clear_layer (l2o);
@ -380,9 +380,9 @@ TEST(4)
tp.set_scale_to_dbu (false);
tp.execute ("test");
EXPECT_EQ (to_s (o, topo, l1o), "polygon (100,200;100,400;300,400;300,200);polygon (1000,2000;1000,4010;3010,4010;3010,2000)");
EXPECT_EQ (to_s (o, topo, l2o), "polygon (100,200;100,400;300,400;300,200)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4010;3010,4010;3010,2000)");
EXPECT_EQ (to_s (o, topo, l1o), "box (100,200;300,400);box (1000,2000;3010,4010)");
EXPECT_EQ (to_s (o, topo, l2o), "box (100,200;300,400)");
EXPECT_EQ (to_s (o, topo, l3o), "box (1000,2000;3010,4010)");
}

View File

@ -5148,9 +5148,6 @@ CODE
m = with_left ? "fill_with_left" : "fill"
# generation of new cells not tested in deep mode
@deep && raise("#{m} command not supported in deep mode currently")
(@engine._output_layout && @engine._output_cell) || raise("#{m} command needs an output layout and output cell")
source = @engine.source

View File

@ -1302,6 +1302,16 @@ TEST(47_fillWithOverlappingBoxesTiled)
run_test (_this, "47", false);
}
TEST(47b_fillWithUsingOutput)
{
run_test (_this, "47b", false);
}
TEST(47bd_fillWithUsingOutputDeep)
{
run_test (_this, "47b", true);
}
TEST(48_drcWithFragments)
{
run_test (_this, "48", false);

View File

@ -266,7 +266,7 @@ BEGIN_PROTECTED
try {
view ()->manager ()->transaction (tl::to_string (QObject::tr ("Export Markers")));
db::Transaction transaction (view ()->is_editable () ? view ()->manager () : 0, tl::to_string (QObject::tr ("Export Markers")));
std::vector <const Category *> categories;
for (rdb::Categories::const_iterator cat = rdb->categories ().begin (); cat != rdb->categories ().end (); ++cat) {
@ -355,11 +355,9 @@ BEGIN_PROTECTED
}
view ()->manager ()->commit ();
view ()->update_content ();
} catch (...) {
view ()->manager ()->commit ();
view ()->update_content ();
throw;
}

BIN
testdata/algo/fill_tool5.gds vendored Normal file

Binary file not shown.

BIN
testdata/algo/fill_tool_au5.oas vendored Normal file

Binary file not shown.

27
testdata/algo/nreader19.cir vendored Normal file
View File

@ -0,0 +1,27 @@
* Test
.options scale=1e-6
.model sky130_fd_pr__pfet_01v8 NMOS level=8 version=3.3.0
.model sky130_fd_pr__nfet_01v8 NMOS level=8 version=3.3.0
XXpmos Q I VDD VDD pmos4_standard w=1.5 l=0.15 nf=4
XXnmos Q I VSS VSS nmos4_standard w=1.5 l=0.15 nf=4
XXDUMMY0 VSS VSS VSS VSS nmos4_standard w=1.5 l=0.15 nf=2
XXDUMMY1 VSS VSS VSS VSS nmos4_standard w=1.5 l=0.15 nf=2
XXDUMMY2 VDD VDD VDD VDD pmos4_standard w=1.5 l=0.15 nf=2
XXDUMMY3 VDD VDD VDD VDD pmos4_standard w=1.5 l=0.15 nf=2
.subckt pmos4_standard D G S B w=0.1 l=0.018 nf=4
MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W='w * nf ' ad='int((nf+1)/2) * W/nf * 0.29' as='int((nf+2)/2) * W/nf * 0.29'
+ pd='2*int((nf+1)/2) * (W/nf + 0.29)' ps='2*int((nf+2)/2) * (W/nf + 0.29)' nrd='0.29 / W' nrs='0.29 / W'
+ m=1
.ends
.subckt nmos4_standard D G S B w=0.1 l=0.018 nf=4
MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W='w * nf ' ad='int((nf+1)/2) * W/nf * 0.29' as='int((nf+2)/2) * W/nf * 0.29'
+ pd='2*int((nf+1)/2) * (W/nf + 0.29)' ps='2*int((nf+2)/2) * (W/nf + 0.29)' nrd='0.29 / W' nrs='0.29 / W'
+ m=1
.ends
.end

23
testdata/drc/drcSimpleTests_47b.drc vendored Normal file
View File

@ -0,0 +1,23 @@
source $drc_test_source
if $drc_test_deep
deep
end
to_fill = input(1, 0)
# Create a fill pattern with a 0.025x0.025 µm box at 2/0
pattern = fill_pattern("FILL_CELL").shape(2, 0, box(0, 0, 0.025, 0.025))
# place every 25 nm
to_fill.fill(pattern, hstep(0.025), vstep(0.025))
# compute remaining parts
l2 = input(2, 0)
(to_fill - l2).output(100, 0)
# we cannot use input(..) on the fill output if we use
# a separate target layout, so wo do this:
layout.layout.write($drc_test_target)

BIN
testdata/drc/drcSimpleTests_47b.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au47b.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au47bd.gds vendored Normal file

Binary file not shown.

View File

@ -625,14 +625,9 @@ class DBNetlist_TestClass < TestBase
dc.terminal_definitions.each { |pd| names << pd.name }
assert_equal(names, [])
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0)
assert_equal(pd.default_value, 2.0)
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1")
pd.default_value = 1.0
assert_equal(pd.default_value, 1.0)
pd.is_primary = false
assert_equal(pd.is_primary?, false)
pd.is_primary = true
assert_equal(pd.is_primary?, true)
dc.add_parameter(pd)
@ -1181,6 +1176,24 @@ END
end
def test_16_deviceParameterObject
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0, false, 17.5, 2.0)
assert_equal(pd.default_value, 2.0)
pd.default_value = 1.0
assert_equal(pd.default_value, 1.0)
assert_equal(pd.is_primary?, false)
pd.is_primary = true
assert_equal(pd.is_primary?, true)
assert_equal(pd.si_scaling, 17.5)
pd.si_scaling = 1.0
assert_equal(pd.si_scaling, 1.0)
assert_equal(pd.geo_scaling_exponent, 2.0)
pd.geo_scaling_exponent = 1.0
assert_equal(pd.geo_scaling_exponent, 1.0)
end
end
load("test_epilogue.rb")

View File

@ -2,10 +2,10 @@
# This script is sourced to define the main version parameters
# The main version
KLAYOUT_VERSION="0.28.5"
KLAYOUT_VERSION="0.28.6"
# The version used for PyPI (don't use variables here!)
KLAYOUT_PYPI_VERSION="0.28.5"
KLAYOUT_PYPI_VERSION="0.28.6"
# The build date
KLAYOUT_VERSION_DATE=$(date "+%Y-%m-%d")