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: on:
pull_request: pull_request:
push: # Running on PRs is enough
# push:
release: release:
types: [published] types: [published]
@ -23,6 +24,13 @@ jobs:
- os: "ubuntu-latest" - os: "ubuntu-latest"
cibuild: "*musllinux*" cibuild: "*musllinux*"
steps: 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 - uses: hmarr/debug-action@v2
- name: Cancel Workflow Action - name: Cancel Workflow Action
uses: styfle/cancel-workflow-action@0.9.1 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): 0.28.5 (2023-02-05):
* Bugfix: %GITHUB%/issues/1275 Region.sized after .smooth returns empty region * 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 klayout (0.28.5-1) unstable; urgency=low
* New features and bugfixes * 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> Author: Kazzz-S<br>
Last modified: 2023-02-01<br> Last modified: 2023-03-19<br>
# 1. Introduction # 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 * 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 * 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 -- * Monterey (12.x) : -- ditto --
@ -73,7 +73,7 @@ You need to have the followings:
``` ```
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
<< Usage of '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 $ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value
@ -130,7 +130,7 @@ $ [python] ./build4mac.py
``` ```
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
<< Usage of '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 $ [python] ./build4mac.py
option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value 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). 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> 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> 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 $ ./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). 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> 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> 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 $ ./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). 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> 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> 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 $ ./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 $ ./makeDMG4mac.py -p LW-qt5MP.pkg.macos-Catalina-release-Rmp32Pmp39 -m
``` ```
This command will generate the two files below:<br> 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.6-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.md5`** ---(2) MD5-value text file
# Known issues # 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> 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 += "---------------------------------------------------------------------------------------------------------\n" usage += "---------------------------------------------------------------------------------------------------------\n"
usage += "<< Usage of 'build4mac.py' >>\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 += "\n"
usage += "$ [python] ./build4mac.py\n" usage += "$ [python] ./build4mac.py\n"
usage += " option & argument : descriptions (refer to 'macbuild/build4mac_env.py' for details)| default value\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'] PymodDistDir = parameters['pymod_dist']
# Using MacPorts # Using MacPorts
if PymodDistDir[ModulePython] == 'dist-MP3': if PymodDistDir[ModulePython] == 'dist-MP3':
addBinPath = "/opt/local/bin"
addIncPath = "/opt/local/include" addIncPath = "/opt/local/include"
addLibPath = "/opt/local/lib" addLibPath = "/opt/local/lib"
# Using Homebrew # Using Homebrew
elif PymodDistDir[ModulePython] == 'dist-HB3': elif PymodDistDir[ModulePython] == 'dist-HB3':
addIncPath = "%s/include" % DefaultHomebrewRoot # defined in "build4mac_env.py" addBinPath = "%s/bin" % DefaultHomebrewRoot # defined in "build4mac_env.py"
addLibPath = "%s/lib" % DefaultHomebrewRoot # defined in "build4mac_env.py" addIncPath = "%s/include" % DefaultHomebrewRoot # -- ditto --
addLibPath = "%s/lib" % DefaultHomebrewRoot # -- ditto --
elif PymodDistDir[ModulePython] == 'dist-ana3': elif PymodDistDir[ModulePython] == 'dist-ana3':
addBinPath = "/Applications/anaconda3/bin"
addIncPath = "/Applications/anaconda3/include" addIncPath = "/Applications/anaconda3/include"
addLibPath = "/Applications/anaconda3/lib" addLibPath = "/Applications/anaconda3/lib"
else: else:
addBinPath = ""
addIncPath = "" addIncPath = ""
addLibPath = "" 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 == "": if not addIncPath == "":
try: try:
cpath = os.environ['CPATH'] cpath = os.environ['CPATH']
@ -937,10 +949,11 @@ def Build_pymod(parameters):
#-------------------------------------------------------------------- #--------------------------------------------------------------------
# [3] Set different command line parameters for building <pymod> # [3] Set different command line parameters for building <pymod>
#-------------------------------------------------------------------- #--------------------------------------------------------------------
cmd1_args = " setup.py build \\\n" cmd1_args = " -m setup build \\\n"
cmd2_args = " setup.py bdist_wheel \\\n" cmd2_args = " -m setup bdist_wheel \\\n"
cmd3_args = " setup.py bdist_egg \\\n" deloc_cmd = " delocate-wheel --ignore-missing-dependencies"
cmd4_args = " setup.py clean --all \\\n" cmd3_args = " <wheel file> \\\n"
cmd4_args = " -m setup clean --all \\\n"
#-------------------------------------------------------------------- #--------------------------------------------------------------------
# [4] Make the consolidated command lines # [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 command2 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
command3 = "time" command3 = "time"
command3 += " \\\n %s \\\n" % parameters['python'] command3 += " \\\n %s \\\n" % deloc_cmd
command3 += cmd3_args command3 += cmd3_args
command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile'] command3 += " 2>&1 | tee -a %s; \\\n" % parameters['logfile']
command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0 command3 += " test ${PIPESTATUS[0]} -eq 0" # tee always exits with 0
@ -986,6 +999,7 @@ def Build_pymod(parameters):
print( "<Stage-4>") print( "<Stage-4>")
print( " ", command4 ) print( " ", command4 )
print( "" ) print( "" )
if parameters['check_cmd_only']: if parameters['check_cmd_only']:
return 0 return 0
@ -1011,11 +1025,26 @@ def Build_pymod(parameters):
print( "", file=sys.stderr ) print( "", file=sys.stderr )
return 1 return 1
ret = subprocess.call( command3, shell=True ) #---------------------------------------------------------------------------------------------------------
# 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: if ret != 0:
print( "", file=sys.stderr ) print( "", file=sys.stderr )
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 )
print( "", file=sys.stderr ) print( "", file=sys.stderr )
return 1 return 1

View File

@ -196,7 +196,7 @@ Ruby31MacPorts = { 'exe': '/opt/local/bin/ruby3.1',
# install with 'sudo port install ruby32' # install with 'sudo port install ruby32'
# [Key Type Name] = 'MP32' # [Key Type Name] = 'MP32'
Ruby32MacPorts = { 'exe': '/opt/local/bin/ruby3.2', 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' 'lib': '/opt/local/lib/libruby.3.2.dylib'
} }

View File

@ -47,6 +47,7 @@ public:
virtual void finish (bool); virtual void finish (bool);
void keep_for_healing (const db::Polygon &poly); void keep_for_healing (const db::Polygon &poly);
void keep_for_healing (const db::Box &box);
private: private:
size_t *mp_count; 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 size_t count () const
{ {
return m_count; return m_count;
@ -110,6 +120,12 @@ HealingCountingReceiver::keep_for_healing (const db::Polygon &poly)
m_for_healing.insert (poly); m_for_healing.insert (poly);
} }
void
HealingCountingReceiver::keep_for_healing (const db::Box &box)
{
m_for_healing.insert (box);
}
void void
HealingCountingReceiver::finish (bool) HealingCountingReceiver::finish (bool)
{ {
@ -132,7 +148,9 @@ public:
void finish (bool /*success*/); void finish (bool /*success*/);
void keep_for_healing (const db::Polygon &poly); 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::Polygon &poly);
void output (const db::Box &poly);
private: private:
db::Layout *mp_layout; 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: private:
const db::Box *mp_tile; const db::Box *mp_tile;
bool m_healing; bool m_healing;
@ -211,12 +246,24 @@ HealingTileLayoutOutputReceiver::keep_for_healing (const db::Polygon &poly)
m_for_healing.insert (poly); m_for_healing.insert (poly);
} }
void
HealingTileLayoutOutputReceiver::keep_for_healing (const db::Box &box)
{
m_for_healing.insert (box);
}
void void
HealingTileLayoutOutputReceiver::output (const db::Polygon &poly) HealingTileLayoutOutputReceiver::output (const db::Polygon &poly)
{ {
mp_cell->shapes (m_layer).insert (poly); mp_cell->shapes (m_layer).insert (poly);
} }
void
HealingTileLayoutOutputReceiver::output (const db::Box &box)
{
mp_cell->shapes (m_layer).insert (box);
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
struct ResultDescriptor struct ResultDescriptor

View File

@ -99,7 +99,7 @@ TEST(1A_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
@ -141,7 +141,7 @@ TEST(1A_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
"Result summary (layers without differences are not shown):\n" "Result summary (layers without differences are not shown):\n"
@ -321,7 +321,7 @@ TEST(2_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"" ""
); );
@ -354,7 +354,7 @@ TEST(2_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"" ""
); );
@ -387,7 +387,7 @@ TEST(3_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -453,7 +453,7 @@ TEST(3_FlatHeal)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -520,7 +520,7 @@ TEST(3_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -553,7 +553,7 @@ TEST(4_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -586,7 +586,7 @@ TEST(4_FlatHeal)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -619,7 +619,7 @@ TEST(4_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -652,7 +652,7 @@ TEST(5_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -685,7 +685,7 @@ TEST(5_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -718,7 +718,7 @@ TEST(6_Flat)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "Layer 10/0 is not present in first layout, but in second\n"
); );
@ -751,7 +751,7 @@ TEST(6_Deep)
reader.read (layout); 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 (), EXPECT_EQ (cap.captured_text (),
"Layer 10/0 is not present in first layout, but in second\n" "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_min_projection (options.min_projection);
check.set_max_projection (options.max_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; std::vector<bool> foreign;
bool has_other = false; bool has_other = false;
bool other_is_merged = true; bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) { if (other == subject_regionptr () || other == foreign_regionptr ()) {
foreign.push_back (other == foreign_regionptr ()); foreign.push_back (other == foreign_regionptr ());
others.push_back (begin_merged ()); others.push_back (polygons);
other_is_merged = primary_is_merged; other_is_merged = primary_is_merged;
} else { } else {
foreign.push_back (false); foreign.push_back (false);

View File

@ -376,15 +376,12 @@ private:
while (cc != current) { while (cc != current) {
rec.finish (cc->first, cc->second); rec.finish (cc->first, cc->second);
typename std::set<std::pair<const Obj *, const Obj *> >::iterator s; auto s = seen.lower_bound (std::make_pair (cc->first, (const Obj *)0));
s = seen.lower_bound (std::make_pair (cc->first, (const Obj *)0)); auto s0 = s;
while (s != seen.end () && s->first == cc->first) { while (s != seen.end () && s->first == cc->first) {
seen.erase (s++); ++s;
}
s = seen.lower_bound (std::make_pair ((const Obj *)0, cc->first));
while (s != seen.end () && s->second == cc->first) {
seen.erase (s++);
} }
seen.erase (s0, s);
++cc; ++cc;
} }
@ -429,8 +426,8 @@ private:
for (iterator_type i = f0; i != f; ++i) { for (iterator_type i = f0; i != f; ++i) {
for (iterator_type j = c; j < i; ++j) { for (iterator_type j = c; j < i; ++j) {
if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) { if (bs_boxes_overlap (bc (*i->first), bc (*j->first), enl)) {
if (seen.insert (std::make_pair (i->first, j->first)).second) { 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 (j->first, i->first)); seen.insert (std::make_pair (i->first, j->first));
rec.add (i->first, i->second, j->first, j->second); rec.add (i->first, i->second, j->first, j->second);
if (rec.stop ()) { if (rec.stop ()) {
return false; return false;
@ -791,21 +788,23 @@ private:
while (cc1 != current1) { while (cc1 != current1) {
rec.finish1 (cc1->first, cc1->second); rec.finish1 (cc1->first, cc1->second);
typename std::set<std::pair<const Obj1 *, const Obj2 *> >::iterator s; auto s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0));
s = seen1.lower_bound (std::make_pair (cc1->first, (const Obj2 *)0)); auto s0 = s;
while (s != seen1.end () && s->first == cc1->first) { while (s != seen1.end () && s->first == cc1->first) {
seen1.erase (s++); ++s;
} }
seen1.erase (s0, s);
++cc1; ++cc1;
} }
while (cc2 != current2) { while (cc2 != current2) {
rec.finish2 (cc2->first, cc2->second); rec.finish2 (cc2->first, cc2->second);
typename std::set<std::pair<const Obj2 *, const Obj1 *> >::iterator s; auto s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0));
s = seen2.lower_bound (std::make_pair (cc2->first, (const Obj1 *)0)); auto s0 = s;
while (s != seen2.end () && s->first == cc2->first) { while (s != seen2.end () && s->first == cc2->first) {
seen2.erase (s++); ++s;
} }
seen2.erase (s0, s);
++cc2; ++cc2;
} }
@ -883,7 +882,7 @@ private:
x = xx; x = xx;
if (m_report_progress) { 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 unsigned int
DeepShapeStore::layout_for_iter (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) 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) { if (l == m_layout_map.end () || m_layouts[l->second] == 0) {
unsigned int layout_index; 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 ()); 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; return layout_index;
} else { } 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) 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) { while (m_layouts.size () <= layout_index) {
m_layouts.push_back (0); 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 ()); 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) 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 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); int cmp_iter = db::compare_iterators_with_respect_to_target_hierarchy (a.first, b.first);
if (cmp_iter != 0) { 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); 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 // no copying
DeepShapeStore (const DeepShapeStore &); DeepShapeStore (const DeepShapeStore &);

View File

@ -146,7 +146,7 @@ public:
* @brief Creates an empty device parameter definition * @brief Creates an empty device parameter definition
*/ */
DeviceParameterDefinition () 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 .. // .. nothing yet ..
} }
@ -154,8 +154,8 @@ public:
/** /**
* @brief Creates a device parameter definition with the given name and description * @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) 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_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 .. // .. nothing yet ..
} }
@ -192,18 +192,10 @@ public:
m_description = d; m_description = d;
} }
/**
* @brief Gets the parameter default value
*/
double default_value () const
{
return m_default_value;
}
/** /**
* @brief Gets the SI unit scaling factor * @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). * scaling factor gives the translation to SI units (1e-6 for micrometers).
*/ */
double si_scaling () const 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) void set_default_value (double d)
{ {
@ -267,6 +295,7 @@ private:
size_t m_id; size_t m_id;
bool m_is_primary; bool m_is_primary;
double m_si_scaling; double m_si_scaling;
double m_geo_scaling;
void set_id (size_t id) void set_id (size_t id)
{ {

View File

@ -487,10 +487,10 @@ DeviceClassResistor::DeviceClassResistor ()
equivalent_terminal_id (terminal_id_A, terminal_id_B); equivalent_terminal_id (terminal_id_A, terminal_id_B);
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0)); 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 ("L", "Length (micrometer)", 0.0, false, 1e-6, 1.0));
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6)); 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)); 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)); 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); equivalent_terminal_id (terminal_id_A, terminal_id_B);
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0)); 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 ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6)); 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 ("A", "Anode"));
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode")); 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 ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6)); 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")); add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
equivalent_terminal_id (terminal_id_D, terminal_id_S); 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 ("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)); 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)); 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)); 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)); 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)); add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
} }
bool bool
@ -886,12 +886,12 @@ DeviceClassBJT3Transistor::DeviceClassBJT3Transistor ()
add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter")); add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter"));
// NOTE: the emitter area and the emitter count are the primary parameters // 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 ("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)); 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)); 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)); 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)); 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)); 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)); 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_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); void read_circuit (tl::Extractor &ex, const std::string &name);
bool read_card (); bool read_card ();
void read_options (tl::Extractor &ex);
void ensure_circuit (); void ensure_circuit ();
std::string get_line (); std::string get_line ();
void error (const std::string &msg); void error (const std::string &msg);
@ -625,6 +626,10 @@ SpiceCircuitDict::read_card ()
std::string nc = read_name (ex, mp_netlist); std::string nc = read_name (ex, mp_netlist);
read_circuit (ex, nc); read_circuit (ex, nc);
} else if (ex.test_without_case (".options")) {
read_options (ex);
} else if (ex.test_without_case (".ends")) { } else if (ex.test_without_case (".ends")) {
return true; return true;
@ -691,6 +696,56 @@ SpiceCircuitDict::read_card ()
return false; 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 void
SpiceCircuitDict::ensure_circuit () SpiceCircuitDict::ensure_circuit ()
{ {
@ -1162,9 +1217,9 @@ SpiceNetlistBuilder::build_global_nets ()
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate) NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_delegate (delegate), m_strict (false) : mp_delegate (delegate), m_strict (false)
{ {
static NetlistSpiceReaderDelegate std_delegate;
if (! 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 <map>
#include <string> #include <string>
#include <memory>
namespace db namespace db
{ {
@ -64,6 +65,7 @@ public:
private: private:
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate; tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
std::unique_ptr<NetlistSpiceReaderDelegate> mp_default_delegate;
bool m_strict; 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 () NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
: mp_netlist (0) : mp_netlist (0), m_options ()
{ {
// .. nothing yet .. // .. nothing yet ..
} }
@ -97,6 +109,22 @@ NetlistSpiceReaderDelegate::~NetlistSpiceReaderDelegate ()
// .. nothing yet .. // .. 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*/) void NetlistSpiceReaderDelegate::start (db::Netlist * /*netlist*/)
{ {
// .. nothing yet .. // .. nothing yet ..
@ -107,7 +135,7 @@ void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
// .. nothing yet .. // .. nothing yet ..
} }
bool NetlistSpiceReaderDelegate::control_statement(const std::string & /*line*/) bool NetlistSpiceReaderDelegate::control_statement (const std::string & /*line*/)
{ {
return false; return false;
} }
@ -196,7 +224,8 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
if (ex.try_read_word (n) && ex.test ("=")) { if (ex.try_read_word (n) && ex.test ("=")) {
// a parameter // 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 { } 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) 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); parse_element_components (s, nn, pv, variables);
// interpret the parameters according to the code // 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::map<std::string, tl::Variant> params = pv;
std::vector<size_t> terminal_order; std::vector<size_t> terminal_order;
size_t defp = std::numeric_limits<size_t>::max ();
double mult = 1.0; double mult = 1.0;
auto mp = params.find ("M"); auto mp = params.find ("M");
if (mp != params.end ()) { 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"))); error (tl::to_string (tr ("A 'R' element requires two or three nets")));
} }
// Apply multiplier // Apply multiplier (divider, according to ngspice manual)
value /= mult; 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") { } else if (element == "L") {
if (nets.size () == 2) { 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"))); error (tl::to_string (tr ("A 'L' element requires two nets")));
} }
// Apply multiplier // Apply multiplier (divider, according to ngspice manual)
value /= mult; value /= mult;
defp = db::DeviceClassInductor::param_id_L;
} else if (element == "C") { } else if (element == "C") {
if (nets.size () == 2) { if (nets.size () == 2) {
@ -432,6 +489,17 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
// Apply multiplier // Apply multiplier
value *= mult; 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") { } else if (element == "D") {
if (cls) { if (cls) {
@ -445,10 +513,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
cls = make_device_class<db::DeviceClassDiode> (circuit, cn); cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
} }
// Apply multiplier to "A" // Apply multiplier
auto p = params.find ("A"); static const char *scale_params[] = { "A", "P" };
if (p != params.end ()) { for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
p->second = tl::Variant (p->second.to_double () * mult); auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
} }
} else if (element == "Q") { } else if (element == "Q") {
@ -479,10 +550,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} }
} }
// Apply multiplier to "AE" // Apply multiplier
auto p = params.find ("AE"); static const char *scale_params[] = { "AE", "PE", "AB", "PB", "AC", "PC" };
if (p != params.end ()) { for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
p->second = tl::Variant (p->second.to_double () * mult); auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
} }
} else if (element == "M") { } else if (element == "M") {
@ -502,10 +576,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} }
} }
// Apply multiplier to "W" // Apply multiplier
auto p = params.find ("W"); static const char *scale_params[] = { "W", "AD", "AS", "PD", "PS" };
if (p != params.end ()) { for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
p->second = tl::Variant (p->second.to_double () * mult); auto p = params.find (scale_params [i]);
if (p != params.end ()) {
p->second = tl::Variant (p->second.to_double () * mult);
}
} }
// issue #1304 // issue #1304
@ -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 (); std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions_non_const ();
for (std::vector<db::DeviceParameterDefinition>::iterator i = pd.begin (); i != pd.end (); ++i) { for (std::vector<db::DeviceParameterDefinition>::iterator i = pd.begin (); i != pd.end (); ++i) {
auto v = params.find (i->name ()); auto v = params.find (i->name ());
double pv = 0.0;
if (v != params.end ()) { 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) { } 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; 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 tl::Variant
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables) 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 DeviceClass;
class Device; 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 * @brief A delegate to handle various forms of devices and translates them
* *
@ -55,6 +63,22 @@ public:
NetlistSpiceReaderDelegate (); NetlistSpiceReaderDelegate ();
virtual ~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 * @brief Called when the netlist reading starts
*/ */
@ -122,7 +146,6 @@ public:
/** /**
* @brief Reads a set of string components and parameters from the string * @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); 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 * @brief External interface for start
*/ */
void do_start () void do_start ();
{
start (mp_netlist);
}
/** /**
* @brief External interface for finish * @brief External interface for finish
*/ */
void do_finish () void do_finish ();
{
finish (mp_netlist);
}
/** /**
* @brief Sets the netlist * @brief Sets the netlist
*/ */
void set_netlist (db::Netlist *netlist) void set_netlist (db::Netlist *netlist);
{
mp_netlist = netlist; /**
} * @brief Applies SI and geometry scaling to the device parameters
*/
void apply_parameter_scaling (db::Device *device) const;
private: private:
db::Netlist *mp_netlist; 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; static variables_type empty_variables;
mp_variables = vars ? vars : &empty_variables; mp_variables = vars ? vars : &empty_variables;
@ -202,7 +203,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
*status = true; *status = true;
} }
double f = 1.0; double f = m_def_scale;
if (*ex == 't' || *ex == 'T') { if (*ex == 't' || *ex == 'T') {
f = 1e12; f = 1e12;
} else if (*ex == 'g' || *ex == 'G') { } else if (*ex == 'g' || *ex == 'G') {

View File

@ -45,7 +45,7 @@ class DB_PUBLIC NetlistSpiceReaderExpressionParser
public: public:
typedef std::map<std::string, tl::Variant> variables_type; 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 (tl::Extractor &ex) const;
tl::Variant read (const std::string &s) const; tl::Variant read (const std::string &s) const;
@ -54,6 +54,7 @@ public:
private: private:
const variables_type *mp_variables; const variables_type *mp_variables;
double m_def_scale;
tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const; tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const;
tl::Variant read_dot_expr (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 // 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 bool
rasterize (const db::Polygon &polygon, db::AreaMap &am) 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) { for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (left); 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); 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) { for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell); 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; 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); 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) { for (std::vector <db::Edge>::iterator e = cc; e != ff; ++e) {
std::pair<bool, db::Edge> ec = e->clipped (cell); 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; 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) { for (std::vector <db::Edge>::iterator e = cc; e != fff; ++e) {
std::pair<bool, db::Edge> wide_ec = e->clipped (wide_cell); 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); a += area_type (wide_ec.second.dy ()) * area_type (px);
} }

View File

@ -52,6 +52,31 @@ public:
mp_shapes->insert (t.transformed (m_trans)); 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) void operator() (const db::EdgePair &ep)
{ {
mp_shapes->insert (ep.normalized ().to_polygon (m_ep_sizing).transformed (m_trans)); mp_shapes->insert (ep.normalized ().to_polygon (m_ep_sizing).transformed (m_trans));

View File

@ -135,6 +135,27 @@ private:
TilingProcessor *mp_proc; 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 * @brief Delivery of tiling processor output
* This utility is put between the container and the receiver. * This utility is put between the container and the receiver.
@ -144,7 +165,10 @@ private:
template <class X> template <class X>
void insert (X &inserter, const db::Polygon &o, const db::Box &tile, bool clip) 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 // apply clipping
if (o.box ().touches (tile)) { if (o.box ().touches (tile)) {
std::vector <db::Polygon> clipped_poly; 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> template <class X>
void insert (X &inserter, const db::SimplePolygon &o, const db::Box &tile, bool clip) 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 // apply clipping
if (o.box ().touches (tile)) { if (o.box ().touches (tile)) {
std::vector <db::SimplePolygon> clipped_poly; 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 * @brief Delivery of tiling processor output
* This utility is put between the container and the receiver. * 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." "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", 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" "@brief Creates a new parameter definition.\n"
"@param name The name of the parameter\n" "@param name The name of the parameter\n"
"@param description The human-readable description\n" "@param description The human-readable description\n"
"@param default_value The initial value\n" "@param default_value The initial value\n"
"@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\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 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, gsi::method ("name", &db::DeviceParameterDefinition::name,
"@brief Gets the name of the parameter." "@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, gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling,
"@brief Gets the scaling factor to SI units.\n" "@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, gsi::method ("id", &db::DeviceParameterDefinition::id,
"@brief Gets the ID of the parameter.\n" "@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" "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", Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "NetlistSpiceReaderDelegate",
gsi::method_ext ("start", &start_fb, "@hide") + gsi::method_ext ("start", &start_fb, "@hide") +
gsi::method_ext ("finish", &finish_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" "@brief Issues an error with the given message.\n"
"Use this method to generate an error." "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 (), "{}"), 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" "@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 " "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(); CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au4c.gds"); 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" " subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
"end;\n" "end;\n"
"circuit 'PMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\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" "end;\n"
"circuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\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" "end;\n"
"circuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\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" "end;\n"
"circuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\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" "end;\n"
); );
} }
@ -828,3 +862,18 @@ TEST(100_ExpressionParser)
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true); EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true);
EXPECT_EQ (v.to_string (), "6"); 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.queue ("_tile && _output(o3, _tile, false)");
tp.execute ("test"); 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, o1), "box (60,10;70,20);box (10,10;30,30)");
EXPECT_EQ (to_s (ly, top, o2), "polygon (50,40;50,70;80,70;80,40)"); EXPECT_EQ (to_s (ly, top, o2), "box (50,40;80,70)");
EXPECT_EQ (to_s (ly, top, o3), ""); EXPECT_EQ (to_s (ly, top, o3), "");
ly.clear_layer (o1); ly.clear_layer (o1);
@ -164,9 +164,9 @@ TEST(2)
tp.execute ("test"); 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, 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.queue ("_output(o3, _tile)");
tp.execute ("test"); 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, o1), "box (60,10;70,20);box (10,10;30,30)");
EXPECT_EQ (to_s (ly, top, o2), "polygon (50,40;50,70;80,70;80,40)"); EXPECT_EQ (to_s (ly, top, o2), "box (50,40;80,70)");
EXPECT_EQ (to_s (ly, top, o3), ""); EXPECT_EQ (to_s (ly, top, o3), "");
ly.clear_layer (o1); ly.clear_layer (o1);
@ -199,9 +199,9 @@ TEST(2)
tp.execute ("test"); 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, 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"); tp.execute ("test");
EXPECT_EQ (to_s (o, topo, l1o), ""); 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, l2o), "box (1000,2000;3000,4000)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4000;3000,4000;3000,2000)"); EXPECT_EQ (to_s (o, topo, l3o), "box (1000,2000;3000,4000)");
o.clear_layer (l1o); o.clear_layer (l1o);
o.clear_layer (l2o); o.clear_layer (l2o);
@ -359,8 +359,8 @@ TEST(4)
tp.execute ("test"); 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, 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, l2o), "box (1000,2000;3000,4000)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4010;3010,4010;3010,2000)"); EXPECT_EQ (to_s (o, topo, l3o), "box (1000,2000;3010,4010)");
o.clear_layer (l1o); o.clear_layer (l1o);
o.clear_layer (l2o); o.clear_layer (l2o);
@ -370,7 +370,7 @@ TEST(4)
tp.execute ("test"); 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 (l1o);
o.clear_layer (l2o); o.clear_layer (l2o);
@ -380,9 +380,9 @@ TEST(4)
tp.set_scale_to_dbu (false); tp.set_scale_to_dbu (false);
tp.execute ("test"); 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, l1o), "box (100,200;300,400);box (1000,2000;3010,4010)");
EXPECT_EQ (to_s (o, topo, l2o), "polygon (100,200;100,400;300,400;300,200)"); EXPECT_EQ (to_s (o, topo, l2o), "box (100,200;300,400)");
EXPECT_EQ (to_s (o, topo, l3o), "polygon (1000,2000;1000,4010;3010,4010;3010,2000)"); 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" 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") (@engine._output_layout && @engine._output_cell) || raise("#{m} command needs an output layout and output cell")
source = @engine.source source = @engine.source

View File

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

View File

@ -266,7 +266,7 @@ BEGIN_PROTECTED
try { 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; std::vector <const Category *> categories;
for (rdb::Categories::const_iterator cat = rdb->categories ().begin (); cat != rdb->categories ().end (); ++cat) { 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 (); view ()->update_content ();
} catch (...) { } catch (...) {
view ()->manager ()->commit ();
view ()->update_content (); view ()->update_content ();
throw; 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 } dc.terminal_definitions.each { |pd| names << pd.name }
assert_equal(names, []) assert_equal(names, [])
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0) pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1")
assert_equal(pd.default_value, 2.0)
pd.default_value = 1.0 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 pd.is_primary = true
assert_equal(pd.is_primary?, true)
dc.add_parameter(pd) dc.add_parameter(pd)
@ -1181,6 +1176,24 @@ END
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 end
load("test_epilogue.rb") load("test_epilogue.rb")

View File

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