c4m-flexcell 0.4.0.dev23__tar.gz → 0.4.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/.gitlab-ci.yml +0 -2
  2. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/PKG-INFO +11 -6
  3. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/README.md +7 -5
  4. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/activecolumnscell.py +169 -115
  5. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/canvas.py +11 -0
  6. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/factory.py +259 -178
  7. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m_flexcell.egg-info/PKG-INFO +12 -7
  8. c4m_flexcell-0.4.2/c4m_flexcell.egg-info/requires.txt +3 -0
  9. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/dev-requirements.txt +1 -0
  10. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/dodo.py +1 -1
  11. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/setup.py +2 -2
  12. c4m_flexcell-0.4.0.dev23/c4m_flexcell.egg-info/requires.txt +0 -2
  13. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/.gitignore +0 -0
  14. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/COPYRIGHT.md +0 -0
  15. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/LICENSE.md +0 -0
  16. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/LICENSES/agpl-3.0.txt +0 -0
  17. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/LICENSES/cern_ohl_s_v2.txt +0 -0
  18. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/LICENSES/gpl-2.0.txt +0 -0
  19. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/ReleaseNotes/v0.0.4.md +0 -0
  20. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/ReleaseNotes/v0.1.0.md +0 -0
  21. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/__init__.py +0 -0
  22. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/cell_canvas.ipynb +0 -0
  23. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/__init__.py +0 -0
  24. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/_waiters.py +0 -0
  25. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m/flexcell/cell.py +0 -0
  26. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m_flexcell.egg-info/SOURCES.txt +0 -0
  27. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m_flexcell.egg-info/dependency_links.txt +0 -0
  28. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/c4m_flexcell.egg-info/top_level.txt +0 -0
  29. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/ci-requirements.txt +0 -0
  30. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/run_unittests.sh +0 -0
  31. {c4m_flexcell-0.4.0.dev23 → c4m_flexcell-0.4.2}/setup.cfg +0 -0
@@ -19,7 +19,6 @@ before_script:
19
19
  dist:
20
20
  only:
21
21
  - main
22
- - dev_v0.4
23
22
  variables:
24
23
  TWINE_PASSWORD: '${CI_JOB_TOKEN}'
25
24
  TWINE_USERNAME: 'gitlab-ci-token'
@@ -34,7 +33,6 @@ dist:
34
33
  dist-test:
35
34
  except:
36
35
  - main
37
- - dev_v0.4
38
36
  script:
39
37
  # Need to fetch tags
40
38
  - git fetch --depth=1 origin +refs/tags/*:refs/tags/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: c4m_flexcell
3
- Version: 0.4.0.dev23
3
+ Version: 0.4.2
4
4
  Summary: PDKMaster based scalable standard cell library
5
5
  Author: Staf Verhaegen
6
6
  Author-email: staf@fibraservi.eu
@@ -10,6 +10,9 @@ Project-URL: Bug Tracker, https://gitlab.com/Chips4Makers/c4m-flexcell/issues
10
10
  Requires-Python: ~=3.6
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE.md
13
+ Requires-Dist: setuptools
14
+ Requires-Dist: nest-asyncio
15
+ Requires-Dist: PDKMaster~=0.10.0
13
16
 
14
17
  # Flexible, scalable, standard cell library
15
18
 
@@ -19,9 +22,13 @@ Standard cells are introduced into an [ASIC](https://en.wikipedia.org/wiki/Appli
19
22
 
20
23
  ## Release History
21
24
 
22
- Current development is done on a development branch for version 0.4. Documentation/README will
23
- be updated together with the release of this version.
24
-
25
+ * [v0.4.2](https://gitlab.com/Chips4Makers/c4m-flexcell/-/commits/v0.10.2): only internal
26
+ improvements
27
+ * v0.4.1:
28
+ major reworking of the code. A new class ActiveColumnsCell has been introduced to generate the layouts of the standard cells. This removes the lambda based dimensions from the cells to compute them all from the design rules.
29
+ All cells were converter to this new layout method and this should allow to make standard cell libraries with much lower height of the cells. Compatiblity function are provided so that layout spec can be derived as was done previously as based on a lambda value.
30
+ v0.4.0 had only dev releases and code was rebased and squashed for this release.
31
+ * v0.3.3: bug fix; remove public 0.3.2 release
25
32
  * v0.3.2: code cleansing, bug fixing, update dependencies
26
33
  * v0.3.1: small update for Coriolis export
27
34
  * v0.3.0: Update for [release v0.9.0 of PDKMaster](https://gitlab.com/Chips4Makers/PDKMaster/-/blob/v0.9.0/ReleaseNotes/v0.9.0.md); replace Library-> StdCellFactory to follow common usage of a factory to generate cells.
@@ -39,7 +46,5 @@ In future options are planned so libraries can be generated for different target
39
46
  ## Status
40
47
 
41
48
  This repository is currently considered experimental code with no backwards compatibility guarantees whatsoever.
42
- This development is done in a development branch for new layout generation code.
43
- When this development is done it will be released as version 0.4.0.
44
49
  Current implementation is based on the topology of the Coriolis nsxlib standard cells with some area improvements but not yet with optimal area use. For v0.1 of this library a total replacement of the layout generation is planned fully based on minimized area for the technology design rules.
45
50
  If interested head over to [gitter](https://gitter.im/Chips4Makers/community) for further discussion.
@@ -6,9 +6,13 @@ Standard cells are introduced into an [ASIC](https://en.wikipedia.org/wiki/Appli
6
6
 
7
7
  ## Release History
8
8
 
9
- Current development is done on a development branch for version 0.4. Documentation/README will
10
- be updated together with the release of this version.
11
-
9
+ * [v0.4.2](https://gitlab.com/Chips4Makers/c4m-flexcell/-/commits/v0.10.2): only internal
10
+ improvements
11
+ * v0.4.1:
12
+ major reworking of the code. A new class ActiveColumnsCell has been introduced to generate the layouts of the standard cells. This removes the lambda based dimensions from the cells to compute them all from the design rules.
13
+ All cells were converter to this new layout method and this should allow to make standard cell libraries with much lower height of the cells. Compatiblity function are provided so that layout spec can be derived as was done previously as based on a lambda value.
14
+ v0.4.0 had only dev releases and code was rebased and squashed for this release.
15
+ * v0.3.3: bug fix; remove public 0.3.2 release
12
16
  * v0.3.2: code cleansing, bug fixing, update dependencies
13
17
  * v0.3.1: small update for Coriolis export
14
18
  * v0.3.0: Update for [release v0.9.0 of PDKMaster](https://gitlab.com/Chips4Makers/PDKMaster/-/blob/v0.9.0/ReleaseNotes/v0.9.0.md); replace Library-> StdCellFactory to follow common usage of a factory to generate cells.
@@ -26,7 +30,5 @@ In future options are planned so libraries can be generated for different target
26
30
  ## Status
27
31
 
28
32
  This repository is currently considered experimental code with no backwards compatibility guarantees whatsoever.
29
- This development is done in a development branch for new layout generation code.
30
- When this development is done it will be released as version 0.4.0.
31
33
  Current implementation is based on the topology of the Coriolis nsxlib standard cells with some area improvements but not yet with optimal area use. For v0.1 of this library a total replacement of the layout generation is planned fully based on minimized area for the technology design rules.
32
34
  If interested head over to [gitter](https://gitter.im/Chips4Makers/community) for further discussion.
@@ -1,7 +1,8 @@
1
1
  # SPDX-License-Identifier: GPL-2.0-or-later OR AGPL-3.0-or-later OR CERN-OHL-S-2.0+
2
2
  from itertools import chain
3
3
  import abc
4
- import asyncio
4
+ import asyncio, nest_asyncio
5
+ from threading import Thread
5
6
  from typing import (
6
7
  List, Tuple, Iterator, Optional, Union, Callable, TypeVar, Generic, cast,
7
8
  )
@@ -1450,7 +1451,6 @@ class _Constraints:
1450
1451
 
1451
1452
  # Place the poly pads that are not in a metal 1 row.
1452
1453
  # self.log("_do_layout: calling _place_polypads")
1453
- # tasks.append(asyncio.create_task(self._place_polypads_m1rows()))
1454
1454
  tasks.extend((
1455
1455
  asyncio.create_task(self._place_polypads_m1row(m1row=m1row))
1456
1456
  for m1row in chain(*self.m1rows)
@@ -1492,11 +1492,6 @@ class _Constraints:
1492
1492
  # self.log("_do_layout: calling _size_width")
1493
1493
  self._size_width()
1494
1494
 
1495
- # Add taps where possible
1496
- # This assumes that the widht of the cell is known
1497
- # self.log("_do_layout: calling _add_taps")
1498
- self._add_taps()
1499
-
1500
1495
  return True
1501
1496
 
1502
1497
  def _set_transistor_nets(self) -> None:
@@ -1878,13 +1873,13 @@ class _Constraints:
1878
1873
  cell = self.cell
1879
1874
  canvas = cell.canvas
1880
1875
  ac_canvas = cell.ac_canvas
1881
- metal1 = canvas._metal1
1882
1876
 
1883
1877
  x: Optional[float] = None
1884
1878
  for multim1col in self.m1columns:
1885
1879
  # self.log(f"_place_m1cols(): computing x of {multim1col.name}")
1886
1880
  x = (
1887
- 0.5*ac_canvas._min_m1col_pitch if (x is None)
1881
+ # First column can contact to polypad so use _firstcol_dx
1882
+ ac_canvas._firstcol_dx if (x is None)
1888
1883
  else x + ac_canvas._min_m1col_pitch
1889
1884
  )
1890
1885
  assert not multim1col.is_placed, "Internal error"
@@ -1958,11 +1953,7 @@ class _Constraints:
1958
1953
  def _size_width(self) -> None:
1959
1954
  cell = self.cell
1960
1955
  canvas = cell.canvas
1961
- ac_canvas = cell.ac_canvas
1962
- layouter = cell.layouter
1963
1956
 
1964
- nimplant = canvas._nimplant
1965
- pimplant = canvas._pimplant
1966
1957
  active = canvas._active
1967
1958
 
1968
1959
  lastcol = self.activecolumns[-1]
@@ -1974,105 +1965,14 @@ class _Constraints:
1974
1965
  if nsd is not None:
1975
1966
  lay = nsd._placed
1976
1967
  bb = lay.bounds(mask=active.mask)
1977
- min_widths.append(bb.right + 0.5*active.min_space)
1968
+ min_widths.append(bb.right + 0.5*canvas._min_active_space)
1978
1969
  if psd is not None:
1979
1970
  lay = psd._placed
1980
1971
  bb = lay.bounds(mask=active.mask)
1981
- min_widths.append(bb.right + 0.5*active.min_space)
1972
+ min_widths.append(bb.right + 0.5*canvas._min_active_space)
1982
1973
  assert len(min_widths) > 0
1983
1974
 
1984
- width = self.cell.set_width(min_width=max(min_widths))
1985
-
1986
- # Draw implants
1987
- if nimplant is not None:
1988
- assert canvas._min_active_nimplant_enc is not None
1989
- assert canvas._min_nsd_enc is not None
1990
- enc = canvas._min_active_nimplant_enc.first
1991
- left = -enc
1992
- right = width + enc
1993
- bottom = ac_canvas._nact_bottom - canvas._min_nsd_enc.second
1994
- top = canvas._well_edge_height
1995
- shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
1996
- layouter.add_portless(prim=nimplant, shape=shape)
1997
- # Draw implants
1998
- if pimplant is not None:
1999
- assert canvas._min_active_pimplant_enc is not None
2000
- assert canvas._min_psd_enc is not None
2001
- enc = canvas._min_active_pimplant_enc.first
2002
- left = -enc
2003
- right = width + enc
2004
- top = ac_canvas._pact_top + canvas._min_psd_enc.second
2005
- bottom = canvas._well_edge_height
2006
- shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
2007
- layouter.add_portless(prim=pimplant, shape=shape)
2008
-
2009
- def _add_taps(self) -> None:
2010
- cell = self.cell
2011
- tech = cell.tech
2012
- canvas = cell.canvas
2013
- layouter = cell.layouter
2014
-
2015
- width = cell.width
2016
-
2017
- pwell = canvas._pwell
2018
- nwell = canvas._nwell
2019
- active = canvas._active
2020
- nimplant = canvas._nimplant
2021
- if canvas._min_active_nimplant_enc is None:
2022
- assert nimplant is None, "Internal error"
2023
- nenc = None
2024
- else:
2025
- nenc = canvas._min_active_nimplant_enc.wide()
2026
- if (nenc.first + _geo.epsilon) < 0.5*active.min_space:
2027
- nenc = _prp.Enclosure((0.5*active.min_space, nenc.second))
2028
- pimplant = canvas._pimplant
2029
- if canvas._min_active_pimplant_enc is None:
2030
- assert pimplant is None, "Internal error"
2031
- penc = None
2032
- else:
2033
- penc = canvas._min_active_pimplant_enc.wide()
2034
- if (penc.first + _geo.epsilon) < 0.5*active.min_space:
2035
- penc = _prp.Enclosure((0.5*active.min_space, penc.second))
2036
- active = canvas._active
2037
- contact = canvas._contact
2038
-
2039
- tap_height = tech.computed.min_width(active, up=True, min_enclosure=True)
2040
-
2041
- # ptap
2042
- act_left = 0.5*active.min_space
2043
- act_right = width - act_left
2044
- act_bottom = 0.5*active.min_space
2045
- act_top = act_bottom + tap_height
2046
- lay = layouter.add_wire(
2047
- net=cell.vss, well_net=cell.pwell_net, wire=contact,
2048
- bottom=active, bottom_well=pwell, bottom_enclosure="wide",
2049
- bottom_implant=pimplant, bottom_implant_enclosure=penc,
2050
- bottom_left=act_left, bottom_bottom=act_bottom,
2051
- bottom_right=act_right, bottom_top=act_top,
2052
- )
2053
- if pimplant is not None:
2054
- bb = lay.bounds(mask=pimplant.mask)
2055
- if (bb.bottom - _geo.epsilon) > 0.0:
2056
- shape = _geo.Rect.from_rect(rect=bb, bottom=0.0)
2057
- layouter.add_portless(prim=pimplant, shape=shape)
2058
-
2059
- # ntap
2060
- act_left = 0.5*active.min_space
2061
- act_right = width - act_left
2062
- act_top = canvas._cell_height - 0.5*active.min_space
2063
- act_bottom = act_top - tap_height
2064
- lay = layouter.add_wire(
2065
- net=cell.vdd, well_net=cell.nwell_net, wire=contact,
2066
- bottom=active, bottom_well=nwell, bottom_enclosure="wide",
2067
- bottom_implant=nimplant, bottom_implant_enclosure=nenc,
2068
- bottom_left=act_left, bottom_bottom=act_bottom,
2069
- bottom_right=act_right, bottom_top=act_top,
2070
- )
2071
- if nimplant is not None:
2072
- bb = lay.bounds(mask=nimplant.mask)
2073
- if (bb.top + _geo.epsilon) < canvas._cell_height:
2074
- shape = _geo.Rect.from_rect(rect=bb, top=canvas._cell_height)
2075
- layouter.add_portless(prim=nimplant, shape=shape)
1975
+ self.cell.set_width(min_width=max(min_widths))
2076
1976
 
2077
1977
  def log(self, *args):
2078
1978
  print(f"[{self.cell.name}:Constraints]", *args)
@@ -2142,8 +2042,14 @@ class _ActiveColumnsCanvas:
2142
2042
  metal1, down=True, up=True, min_enclosure=True,
2143
2043
  )
2144
2044
 
2145
- dx_act = 0.5*active.min_space + 0.5*self._actcont_width
2045
+ dx_act = 0.5*canvas._min_active_space + 0.5*self._actcont_width
2146
2046
  dx_poly = 0.5*poly.min_space + 0.5*self._polycont_width
2047
+ try:
2048
+ s = tech.computed.min_space(active, poly)
2049
+ except:
2050
+ pass
2051
+ else:
2052
+ dx_poly = max(dx_poly, -0.5*canvas._min_active_space + s + 0.5*self._polycont_width)
2147
2053
  dx_m1 = 0.5*metal1.min_space + 0.5*self._m1col_width
2148
2054
  self._firstcol_dx = tech.on_grid(
2149
2055
  max(dx_act, dx_poly, dx_m1), rounding="ceiling",
@@ -2197,7 +2103,7 @@ class _ActiveColumnsCanvas:
2197
2103
  )
2198
2104
  # Min dx for gap in active
2199
2105
  self._min_actgap_dx = tech.on_grid(
2200
- 0.5*(self._actcont_width + active.min_space),
2106
+ 0.5*(self._actcont_width + canvas._min_active_space),
2201
2107
  rounding="ceiling",
2202
2108
  )
2203
2109
 
@@ -2260,22 +2166,170 @@ class _ActiveColumnsCanvas:
2260
2166
  )
2261
2167
 
2262
2168
 
2263
- class ActiveColumnsCell(_Cell, abc.ABC):
2264
- def __init__(self, *, name: str, fab: "_scfab.StdCellFactory"):
2169
+ class ActiveColumnsCellFrame(_Cell):
2170
+ """Subclasses of this class need to call _draw_frame() after they have set
2171
+ the of the cell.
2172
+ """
2173
+ def __init__(self, *, name: str, fab: "_scfab.StdCellFactory", draw_implants: bool=True):
2265
2174
  super().__init__(name=name, fab=fab)
2266
2175
 
2267
2176
  canvas = fab.canvas
2268
2177
  if canvas._ac_canvas is None:
2269
2178
  canvas._ac_canvas = _ActiveColumnsCanvas(fab=fab)
2270
2179
 
2271
- self._waiterfab = _WaiterFactory(name=f"{name}:waiterfab")
2272
-
2273
- generator = self.build_generator()
2274
- asyncio.get_event_loop().run_until_complete(generator._do_layout())
2180
+ self._draw_implants = draw_implants
2181
+ self._vsstap_lay: Optional[_lay.LayoutT] = None
2182
+ self._vddtap_lay: Optional[_lay.LayoutT] = None
2275
2183
 
2276
2184
  @property
2277
2185
  def ac_canvas(self) -> _ActiveColumnsCanvas:
2278
2186
  return self.canvas._ac_canvas
2187
+ @property
2188
+ def draw_implants(self) -> bool:
2189
+ return self._draw_implants
2190
+ @property
2191
+ def vsstap_lay(self) -> _lay.LayoutT:
2192
+ if self._vsstap_lay is None:
2193
+ raise TypeError(
2194
+ f"Accessing vsstap_lay on cell '{self.name}' before set_width has been called"
2195
+ )
2196
+ return self._vsstap_lay
2197
+ @property
2198
+ def vddtap_lay(self) -> _lay.LayoutT:
2199
+ if self._vddtap_lay is None:
2200
+ raise TypeError(
2201
+ f"Accessing vsstap_lay on cell '{self.name}' before set_width has been called"
2202
+ )
2203
+ return self._vddtap_lay
2204
+
2205
+ def set_width(self, **args) -> float:
2206
+ width = super().set_width(**args)
2207
+
2208
+ tech = self.tech
2209
+ canvas = self.canvas
2210
+ ac_canvas = self.ac_canvas
2211
+ layouter = self.layouter
2212
+
2213
+ pwell = canvas._pwell
2214
+ nwell = canvas._nwell
2215
+ active = canvas._active
2216
+ nimplant = canvas._nimplant
2217
+ if canvas._min_active_nimplant_enc is None:
2218
+ assert nimplant is None, "Internal error"
2219
+ nenc = None
2220
+ else:
2221
+ nenc = canvas._min_active_nimplant_enc.wide()
2222
+ if (nenc.first + _geo.epsilon) < 0.5*canvas._min_active_space:
2223
+ nenc = _prp.Enclosure((0.5*canvas._min_active_space, nenc.second))
2224
+ pimplant = canvas._pimplant
2225
+ if canvas._min_active_pimplant_enc is None:
2226
+ assert pimplant is None, "Internal error"
2227
+ penc = None
2228
+ else:
2229
+ penc = canvas._min_active_pimplant_enc.wide()
2230
+ if (penc.first + _geo.epsilon) < 0.5*canvas._min_active_space:
2231
+ penc = _prp.Enclosure((0.5*canvas._min_active_space, penc.second))
2232
+ active = canvas._active
2233
+ contact = canvas._contact
2234
+
2235
+ tap_height = tech.computed.min_width(active, up=True, min_enclosure=True)
2236
+
2237
+ # ptap
2238
+ act_left = 0.5*canvas._min_active_space
2239
+ act_right = width - act_left
2240
+ act_bottom = 0.5*canvas._min_active_space
2241
+ act_top = act_bottom + tap_height
2242
+ self._vsstap_lay = lay = layouter.add_wire(
2243
+ net=self.vss, well_net=self.pwell_net, wire=contact,
2244
+ bottom=active, bottom_well=pwell, bottom_enclosure="wide",
2245
+ bottom_implant=pimplant, bottom_implant_enclosure=penc,
2246
+ bottom_left=act_left, bottom_bottom=act_bottom,
2247
+ bottom_right=act_right, bottom_top=act_top,
2248
+ )
2249
+ if pimplant is not None:
2250
+ bb = lay.bounds(mask=pimplant.mask)
2251
+ args = {}
2252
+ if (bb.bottom - _geo.epsilon) > 0.0:
2253
+ args["bottom"] = 0.0
2254
+ if (bb.left - _geo.epsilon) > 0.0:
2255
+ args["left"] = 0.0
2256
+ args["right"] = width
2257
+ if args:
2258
+ shape = _geo.Rect.from_rect(rect=bb, **args)
2259
+ layouter.add_portless(prim=pimplant, shape=shape)
2260
+
2261
+ # ntap
2262
+ act_left = 0.5*canvas._min_active_space
2263
+ act_right = width - act_left
2264
+ act_top = canvas._cell_height - 0.5*canvas._min_active_space
2265
+ act_bottom = act_top - tap_height
2266
+ self._vddtap_lay = lay = layouter.add_wire(
2267
+ net=self.vdd, well_net=self.nwell_net, wire=contact,
2268
+ bottom=active, bottom_well=nwell, bottom_enclosure="wide",
2269
+ bottom_implant=nimplant, bottom_implant_enclosure=nenc,
2270
+ bottom_left=act_left, bottom_bottom=act_bottom,
2271
+ bottom_right=act_right, bottom_top=act_top,
2272
+ )
2273
+ if nimplant is not None:
2274
+ bb = lay.bounds(mask=nimplant.mask)
2275
+ args = {}
2276
+ if (bb.top + _geo.epsilon) < canvas._cell_height:
2277
+ args["top"] = canvas._cell_height
2278
+ if (bb.left - _geo.epsilon) > 0.0:
2279
+ args["left"] = 0.0
2280
+ args["right"] = width
2281
+ if args:
2282
+ shape = _geo.Rect.from_rect(rect=bb, **args)
2283
+ layouter.add_portless(prim=nimplant, shape=shape)
2284
+
2285
+ if self.draw_implants:
2286
+ # transistor nimplant
2287
+ if nimplant is not None:
2288
+ assert canvas._min_active_nimplant_enc is not None
2289
+ assert canvas._min_nsd_enc is not None
2290
+ enc = canvas._min_active_nimplant_enc.first
2291
+ left = min(0.0, 0.5*canvas._min_active_space - enc)
2292
+ right = max(width, width - 0.5*canvas._min_active_space + enc)
2293
+ bottom = ac_canvas._nact_bottom - canvas._min_nsd_enc.second
2294
+ top = canvas._well_edge_height
2295
+ shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
2296
+ lay = layouter.add_portless(prim=nimplant, shape=shape)
2297
+
2298
+ # transistor pimplant
2299
+ if pimplant is not None:
2300
+ assert canvas._min_active_pimplant_enc is not None
2301
+ assert canvas._min_psd_enc is not None
2302
+ enc = canvas._min_active_pimplant_enc.first
2303
+ left = min(0.0, 0.5*canvas._min_active_space - enc)
2304
+ right = max(width, width - 0.5*canvas._min_active_space + enc)
2305
+ top = ac_canvas._pact_top + canvas._min_psd_enc.second
2306
+ bottom = canvas._well_edge_height
2307
+ shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
2308
+ layouter.add_portless(prim=pimplant, shape=shape)
2309
+
2310
+ return width
2311
+
2312
+
2313
+ class ActiveColumnsCell(ActiveColumnsCellFrame, abc.ABC):
2314
+ def __init__(self, *, name: str, fab: "_scfab.StdCellFactory"):
2315
+ super().__init__(name=name, fab=fab)
2316
+
2317
+ self._waiterfab = _WaiterFactory(name=f"{name}:waiterfab")
2318
+
2319
+ generator = self.build_generator()
2320
+ loop = asyncio.get_event_loop()
2321
+
2322
+ if loop.is_running():
2323
+ class DoLayoutThread(Thread):
2324
+ def run(self):
2325
+ loop = asyncio.new_event_loop()
2326
+ loop.run_until_complete(generator._do_layout())
2327
+
2328
+ thread = DoLayoutThread()
2329
+ thread.start()
2330
+ thread.join()
2331
+ else:
2332
+ loop.run_until_complete(generator._do_layout())
2279
2333
 
2280
2334
  @abc.abstractmethod
2281
2335
  def build_generator(self) -> ConstraintsT:
@@ -163,6 +163,17 @@ class StdCellCanvas:
163
163
  "Different poly wire for nmos and pmos transistor"
164
164
  )
165
165
 
166
+ vs = [active.min_space]
167
+ if inside is not None:
168
+ for in_ in inside:
169
+ try:
170
+ s = tech.computed.min_space(active.in_(in_))
171
+ except:
172
+ pass
173
+ else:
174
+ vs.append(s)
175
+ self._min_active_space = max(vs)
176
+
166
177
  self._min_active_poly_space = tech.computed.min_space(active, poly)
167
178
 
168
179
  self._nactive = nactive = active if nimplant is None else active.in_(nimplant)
@@ -1,10 +1,10 @@
1
1
  # SPDX-License-Identifier: GPL-2.0-or-later OR AGPL-3.0-or-later OR CERN-OHL-S-2.0+
2
2
  from itertools import product
3
- from typing import List, Tuple, Optional
3
+ from typing import List, Tuple, Dict, Iterable, Optional, Any, cast
4
4
 
5
- from pdkmaster.technology import geometry as _geo
5
+ from pdkmaster.technology import property_ as _prp, geometry as _geo
6
6
  from pdkmaster.design import (
7
- circuit as _ckt, layout as _lay, library as _lbry, factory as _fab,
7
+ circuit as _ckt, layout as _lay, cell as _cell, library as _lbry, factory as _fab,
8
8
  )
9
9
  from pdkmaster.io.coriolis import CoriolisExportSpec
10
10
 
@@ -43,28 +43,48 @@ class _Fill(_Cell):
43
43
 
44
44
  canvas = fab.canvas
45
45
 
46
+ nimplant = canvas._nimplant
47
+ pimplant = canvas._pimplant
48
+
46
49
  self.set_width(width=width*canvas._cell_horplacement_grid)
47
50
 
51
+ layouter = self.layouter
52
+
53
+ if pimplant is not None:
54
+ shape = _geo.Rect(
55
+ left=0.0, right=self.width,
56
+ bottom=0.0, top=pimplant.min_width,
57
+ )
58
+ layouter.add_portless(prim=pimplant, shape=shape)
48
59
 
49
- class _Tie(_Cell):
60
+ if nimplant is not None:
61
+ shape = _geo.Rect(
62
+ left=0.0, right=self.width,
63
+ bottom=(canvas._cell_height - nimplant.min_width),
64
+ top=canvas._cell_height,
65
+ )
66
+ layouter.add_portless(prim=nimplant, shape=shape)
67
+
68
+
69
+ class _Tie(_acc.ActiveColumnsCellFrame):
50
70
  def __init__(self, *,
51
71
  fab: "StdCellFactory", name: str, max_diff: bool=False, max_poly: bool=False, width: int=1,
52
72
  ):
53
73
  if max_diff and max_poly:
54
74
  raise ValueError("only one of max_diff and max_poly can be 'True'")
55
75
 
76
+ super().__init__(fab=fab, name=name, draw_implants=(not max_diff))
77
+
56
78
  canvas = fab.canvas
79
+ ac_canvas = self.ac_canvas
57
80
  tech = fab.tech
58
81
 
59
- super().__init__(fab=fab, name=name)
60
-
61
82
  cell_width = width*canvas._cell_horplacement_grid
62
83
  self.set_width(width=cell_width)
63
84
 
64
85
  active = canvas._active
65
86
  nimplant = canvas._nimplant
66
87
  pimplant = canvas._pimplant
67
- nwell = canvas._nwell
68
88
  poly = canvas._poly
69
89
  contact = canvas._contact
70
90
  metal1 = canvas._metal1
@@ -75,42 +95,21 @@ class _Tie(_Cell):
75
95
  vdd = ckt.nets.vdd
76
96
  vss = ckt.nets.vss
77
97
 
78
- min_actwell_space = tech.computed.min_space(primitive1=active, primitive2=nwell)
79
98
  min_actpoly_space = tech.computed.min_space(primitive1=active, primitive2=poly)
80
99
 
81
100
  if not max_diff:
82
- # vss min tap
83
- l_ch = layouter.wire_layout(
84
- net=vss, wire=contact, rows=canvas._min_tap_chs,
85
- bottom=active, bottom_implant=pimplant, bottom_enclosure="wide",
86
- )
87
- impl_bb = l_ch.bounds(mask=pimplant.mask)
88
- x = 0.5*cell_width
89
- y = 0.5*pimplant.min_space - impl_bb.bottom
90
- vss_tap_lay = layouter.place(object_=l_ch, x=x, y=y)
91
-
92
- # vdd min tap
93
- l_ch = layouter.wire_layout(
94
- net=vdd, well_net=vdd, wire=contact, rows=canvas._min_tap_chs,
95
- bottom=active, bottom_implant=nimplant, bottom_well=nwell,
96
- bottom_enclosure="wide",
97
- )
98
- x = 0.5*cell_width
99
- if nimplant is not None:
100
- impl_bb = l_ch.bounds(mask=nimplant.mask)
101
- y = canvas._cell_height - 0.5*nimplant.min_space - impl_bb.top
102
- else:
103
- # TODO: Proper value
104
- act_bb = l_ch.bounds(mask=active.mask)
105
- y = canvas._cell_height - active.min_space - act_bb.top
106
- vdd_tap_lay = layouter.place(object_=l_ch, x=x, y=y)
107
-
108
101
  if max_poly:
109
102
  # Add big poly
110
- vss_dio_tap_bb = vss_tap_lay.bounds(mask=active.mask)
111
- vdd_dio_tap_bb = vdd_tap_lay.bounds(mask=active.mask)
103
+ vss_dio_tap_bb = self.vsstap_lay.bounds(mask=active.mask)
104
+ vdd_dio_tap_bb = self.vddtap_lay.bounds(mask=active.mask)
112
105
  left = 0.5*poly.min_space
113
- right = cell_width - 0.5*poly.min_space
106
+ try:
107
+ s = tech.computed.min_space(active, poly)
108
+ except:
109
+ pass
110
+ else:
111
+ left = max(left, -0.5*canvas._min_active_space + s)
112
+ right = cell_width - left
114
113
  bottom = vss_dio_tap_bb.top + min_actpoly_space
115
114
  top = vdd_dio_tap_bb.bottom - min_actpoly_space
116
115
  layouter.add_wire(net=vss, wire=poly, shape=_geo.Rect(
@@ -134,96 +133,92 @@ class _Tie(_Cell):
134
133
  rect=vss_polych_m1_bb, bottom=0.0,
135
134
  ))
136
135
  else: # max_diff == True
137
- # vss tap
138
- # First create ch to compute dimension of big diode
136
+ args: Dict[str, Any]
137
+
138
+ # extend vss tap
139
+ top = canvas._well_edge_height - ac_canvas._min_nact_well_enclosure
140
+ s = 0.5*canvas._min_active_space
139
141
  if pimplant is not None:
140
- l_ch = layouter.wire_layout(
141
- net=vss, wire=contact, bottom=active, bottom_implant=pimplant,
142
- )
143
- impl_bb = l_ch.bounds(mask=pimplant.mask)
144
- impl_w = impl_bb.width
145
- # Compute the possible hor. expansion
146
- d = cell_width - impl_w - pimplant.min_space
147
- if tech.on_grid(d) < 0.0:
148
- raise NotEnoughRoom("_Tie cell width")
149
- act_bb = l_ch.bounds(mask=active.mask)
150
- # Maximize width
151
- act_w = act_bb.width + d
152
- # Maximize height
153
- enc = act_bb.bottom - impl_bb.bottom
154
- bottom = 0.5*pimplant.min_space + enc
155
- top = canvas._well_edge_height - min_actwell_space
156
- act_h = tech.on_grid(
157
- top - bottom, mult=2, rounding="floor",
142
+ idx = active.implant.index(pimplant)
143
+ enc = active.min_implant_enclosure[idx].tall()
144
+ args = {"implant_enclosure": enc}
145
+ s = max(s, enc.first)
146
+ if (s - _geo.epsilon) > enc.first:
147
+ enc = _prp.Enclosure((s, enc.second))
148
+ top = min(top, canvas._well_edge_height - pimplant.min_space - enc.second)
149
+ else:
150
+ assert nimplant is not None
151
+ enc = None
152
+ args = {}
153
+ s = max(
154
+ s, tech.computed.min_space(primitive1=active, primitive2=nimplant),
158
155
  )
156
+ if nimplant is not None:
157
+ impl_bb = self.vddtap_lay.bounds(mask=nimplant.mask)
158
+ ext = max(0.0, -impl_bb.left)
159
159
  else:
160
- act_w = cell_width - 2*active.min_space
161
- bottom = active.min_space
162
- top = canvas._well_edge_height - min_actwell_space
163
- act_h = tech.on_grid(
164
- top - bottom, mult=2, rounding="floor",
160
+ assert pimplant is not None
161
+ ext = max(
162
+ 0.0,
163
+ tech.computed.min_space(
164
+ primitive1=active, primitive2=pimplant
165
+ ) - 0.5*canvas._min_active_space
165
166
  )
166
-
167
- # diode with right dimensions
168
- l_ch = layouter.wire_layout(
169
- net=vss, wire=contact, bottom=active, bottom_implant=pimplant,
170
- bottom_width=act_w, bottom_height=act_h,
167
+ act_w = cell_width - 2*ext - 2*s
168
+ if act_w + _geo.epsilon < active.min_width:
169
+ raise NotEnoughRoom(f"maximum Tie with w == {width}")
170
+ bottom = 0.5*canvas._min_active_space
171
+ shape = _geo.Rect(
172
+ left=0.5*(cell_width - act_w), bottom=bottom,
173
+ right=0.5*(cell_width + act_w), top=top,
174
+ )
175
+ layouter.add_wire(
176
+ net=vss, wire=active, shape=shape, implant=pimplant, **args,
171
177
  )
172
- x = 0.5*cell_width
173
- if pimplant is not None:
174
- impl_bb = l_ch.bounds(mask=pimplant.mask)
175
- y = 0.5*pimplant.min_space - impl_bb.bottom
176
- else:
177
- act_bb = l_ch.bounds(mask=active.mask)
178
- y = active.min_space - act_bb.bottom
179
- layouter.place(object_=l_ch, x=x, y=y)
180
178
 
181
- # vdd tap
182
- # First create ch to compute dimension of big diode
179
+ # extend vdd tap
180
+ bottom = canvas._well_edge_height + ac_canvas._min_pact_well_enclosure
181
+ s = 0.5*canvas._min_active_space
183
182
  if nimplant is not None:
184
- l_ch = layouter.wire_layout(
185
- net=vdd, well_net=vdd, wire=contact,
186
- bottom=active, bottom_implant=nimplant, bottom_well=nwell,
187
- )
188
- impl_bb = l_ch.bounds(mask=nimplant.mask)
189
- impl_w = impl_bb.width
190
- # Compute the possible hor. expansion
191
- d = cell_width - impl_w - nimplant.min_space
192
- assert tech.on_grid(d) >= 0.0
193
- act_bb = l_ch.bounds(mask=active.mask)
194
- act_w = act_bb.width + d
195
- # Maximize height
196
- enc = act_bb.bottom - impl_bb.bottom
197
- bottom = canvas._well_edge_height + canvas._min_active_well_enclosure
198
- top = canvas._cell_height - 0.5*nimplant.min_space - enc
199
- act_h = tech.on_grid(
200
- top - bottom, mult=2, rounding="floor",
183
+ idx = active.implant.index(nimplant)
184
+ enc = active.min_implant_enclosure[idx].tall()
185
+ args = {"implant_enclosure": enc}
186
+ s = max(s, enc.first)
187
+ if (s - _geo.epsilon) > enc.first:
188
+ enc = _prp.Enclosure((s, enc.second))
189
+ bottom = max(bottom, canvas._well_edge_height + nimplant.min_space + enc.second)
190
+ else:
191
+ assert pimplant is not None
192
+ enc = None
193
+ args = {}
194
+ s = max(
195
+ s, tech.computed.min_space(primitive1=active, primitive2=pimplant),
201
196
  )
197
+ if pimplant is not None:
198
+ impl_bb = self.vsstap_lay.bounds(mask=pimplant.mask)
199
+ ext = max(0.0, -impl_bb.left)
202
200
  else:
203
- act_w = cell_width - 2*active.min_space
204
- bottom = canvas._well_edge_height + canvas._min_active_well_enclosure
205
- top = canvas._cell_height - active.min_space
206
- act_h = tech.on_grid(
207
- top - bottom, mult=2, rounding="floor",
201
+ assert nimplant is not None
202
+ ext = max(
203
+ 0.0,
204
+ tech.computed.min_space(
205
+ primitive1=active, primitive2=nimplant
206
+ ) - 0.5*canvas._min_active_space
208
207
  )
209
-
210
- # diode with right dimensions
211
- l_ch = layouter.wire_layout(
212
- net=vdd, well_net=vdd, wire=contact,
213
- bottom=active, bottom_implant=nimplant, bottom_well=nwell,
214
- bottom_width=act_w, bottom_height=act_h,
208
+ act_w = cell_width - 2*ext - 2*s
209
+ if act_w + _geo.epsilon < active.min_width:
210
+ raise NotEnoughRoom(f"maximum Tie with w == {width}")
211
+ top = canvas._cell_height - 0.5*canvas._min_active_space
212
+ shape = _geo.Rect(
213
+ left=0.5*(cell_width - act_w), bottom=bottom,
214
+ right=0.5*(cell_width + act_w), top=top,
215
+ )
216
+ layouter.add_wire(
217
+ net=vdd, wire=active, shape=shape, implant=nimplant, **args,
215
218
  )
216
- x = 0.5*cell_width
217
- if nimplant is not None:
218
- impl_bb = l_ch.bounds(mask=nimplant.mask)
219
- y = canvas._cell_height - 0.5*nimplant.min_space - impl_bb.top
220
- else:
221
- act_bb = l_ch.bounds(mask=active.mask)
222
- y = canvas._cell_height - active.min_space - act_bb.top
223
- layouter.place(object_=l_ch, x=x, y=y)
224
219
 
225
220
 
226
- class _Diode(_Cell):
221
+ class _Diode(_acc.ActiveColumnsCellFrame):
227
222
  def __init__(self, *,
228
223
  fab: "StdCellFactory", name: str,
229
224
  ):
@@ -239,6 +234,9 @@ class _Diode(_Cell):
239
234
  nimplant = canvas._nimplant
240
235
  pimplant = canvas._pimplant
241
236
  nwell = canvas._nwell
237
+ nwell_net = self.nwell_net
238
+ pwell = canvas._pwell
239
+ pwell_net = self.pwell_net
242
240
  contact = canvas._contact
243
241
  metal1 = canvas._metal1
244
242
  metal1pin = canvas._metal1pin
@@ -246,54 +244,25 @@ class _Diode(_Cell):
246
244
  ckt = self.circuit
247
245
  layouter = self._layouter
248
246
 
249
- vdd = ckt.nets.vdd
250
- vss = ckt.nets.vss
251
-
252
247
  i_ = ckt.new_net(name="i", external=True)
253
248
 
254
249
  min_actwell_space = tech.computed.min_space(primitive1=active, primitive2=nwell)
255
- try:
256
- s = tech.computed.min_space(primitive1=active.in_(nimplant), primitive2=nwell)
257
- except:
258
- pass
259
- else:
260
- min_actwell_space = max(min_actwell_space, s)
250
+ if nimplant is not None:
251
+ try:
252
+ s = tech.computed.min_space(primitive1=active.in_(nimplant), primitive2=nwell)
253
+ except:
254
+ pass
255
+ else:
256
+ min_actwell_space = max(min_actwell_space, s)
261
257
  enc = active.min_substrate_enclosure
262
258
  if enc is not None:
263
259
  min_actwell_space = max(min_actwell_space, enc.max())
264
260
 
265
261
  # Min active space without implants overlapping
266
- # vss min tap
267
- l_ch = layouter.wire_layout(
268
- net=vss, wire=contact, rows=canvas._min_tap_chs,
269
- bottom=active, bottom_implant=pimplant,
270
- )
271
- x = 0.5*cell_width
272
- if pimplant is not None:
273
- impl_bb = l_ch.bounds(mask=pimplant.mask)
274
- y = 0.5*pimplant.min_space - impl_bb.bottom
275
- else:
276
- act_bb = l_ch.bounds(mask=active.mask)
277
- y = active.min_space - act_bb.bottom
278
- vss_tap_lay = layouter.place(object_=l_ch, x=x, y=y)
279
- vss_tap_act_bb = vss_tap_lay.bounds(mask=active.mask)
280
-
281
- # vdd min tap
282
- l_ch = layouter.wire_layout(
283
- net=vdd, well_net=vdd, wire=contact, rows=canvas._min_tap_chs,
284
- bottom=active, bottom_implant=nimplant, bottom_well=nwell
285
- )
286
- x = 0.5*cell_width
287
- if nimplant is not None:
288
- impl_bb = l_ch.bounds(mask=nimplant.mask)
289
- y = canvas._cell_height - 0.5*nimplant.min_space - impl_bb.top
290
- else:
291
- act_bb = l_ch.bounds(mask=active.mask)
292
- y = canvas._cell_height - active.min_space - act_bb.top
293
- vdd_tap_lay = layouter.place(object_=l_ch, x=x, y=y)
294
- vdd_tap_act_bb = vdd_tap_lay.bounds(mask=active.mask)
262
+ vss_tap_act_bb = self.vsstap_lay.bounds(mask=active.mask)
263
+ vdd_tap_act_bb = self.vddtap_lay.bounds(mask=active.mask)
295
264
 
296
- # vss diode
265
+ # ndiode
297
266
  bottom = max(
298
267
  vss_tap_act_bb.top + canvas._min_nactive_pactive_space_maxenc,
299
268
  canvas._m1_vssrail_width + metal1.min_space,
@@ -303,12 +272,13 @@ class _Diode(_Cell):
303
272
  vss_dio_x = 0.5*cell_width
304
273
  vss_dio_y = bottom + 0.5*h
305
274
  vss_dio_lay = layouter.add_wire(
306
- net=vss, wire=contact, bottom=active, bottom_implant=nimplant,
275
+ net=i_, well_net=pwell_net, wire=contact,
276
+ bottom=active, bottom_implant=nimplant, bottom_well=pwell,
307
277
  x=vss_dio_x, y=vss_dio_y, bottom_height=h, top_height=h,
308
278
  )
309
279
  vss_dio_m1_bb = vss_dio_lay.bounds(mask=metal1.mask)
310
280
 
311
- # vdd diode
281
+ # pdiode
312
282
  bottom = canvas._well_edge_height + canvas._min_active_well_enclosure
313
283
  top = min(
314
284
  vdd_tap_act_bb.bottom - canvas._min_nactive_pactive_space_maxenc,
@@ -318,7 +288,7 @@ class _Diode(_Cell):
318
288
  vdd_dio_x = 0.5*cell_width
319
289
  vdd_dio_y = top - 0.5*h
320
290
  vdd_dio_lay = layouter.add_wire(
321
- net=vdd, well_net=vdd, wire=contact,
291
+ net=i_, well_net=nwell_net, wire=contact,
322
292
  bottom=active, bottom_implant=pimplant, bottom_well=nwell,
323
293
  x=vdd_dio_x, y=vdd_dio_y, bottom_height=h, top_height=h,
324
294
  )
@@ -1805,19 +1775,33 @@ class _Xor2(_acc.ActiveColumnsCell):
1805
1775
  name="q_pin", elements=(q_m1knot0, q_m1knot1), top=net1_psd0,
1806
1776
  )
1807
1777
 
1778
+ i1_nmos0: Optional[_acc.NMOST] = None
1779
+ i1_nmos0pad: Optional[_acc.PolyContactT] = None
1780
+ i1_nmospad: Optional[_acc.PolyContactT] = None
1781
+ i1_pmos0: Optional[_acc.PMOST] = None
1782
+ i1_pmos0pad: Optional[_acc.PolyContactT] = None
1783
+ i1_n_nmos: Optional[_acc.NMOST] = None
1784
+ i1_n_nmospad: Optional[_acc.PolyContactT] = None
1785
+ i1_n_pmos: Optional[_acc.PMOST] = None
1786
+ i1_n_pmospad: Optional[_acc.PolyContactT] = None
1787
+ i1_nmosrow0: Optional[_acc.PolyRowT] = None
1788
+ i1_nmosrow1: Optional[_acc.PolyRowT] = None
1789
+ i1_pmosrow0: Optional[_acc.PolyRowT] = None
1790
+ i1_pmosrow1: Optional[_acc.PolyRowT] = None
1791
+
1808
1792
  if not self.inverted:
1809
1793
  i1_nmos0 = self.nmos(name="i1_nmos0", net=i1, w_size="min")
1810
1794
  i1_nmos0pad = self.polypad(
1811
1795
  name="i1_nmos0pad", net=i1, left=(i0_pass0, q_pin),
1812
1796
  )
1813
- i1_nmos0row = self.polyrow(
1797
+ i1_nmosrow0 = self.polyrow(
1814
1798
  name="i1_nmos0row", elements=(i1_nmos0pad, i1_nmos0),
1815
1799
  )
1816
1800
  i1_n_pmos = self.pmos(name="i1_n_pmos", net=_i1_n, w_size="min")
1817
1801
  i1_n_pmospad = self.polypad(
1818
1802
  name="i1_n_pmospad", net=_i1_n, left=(i0_pass0, q_pin),
1819
1803
  )
1820
- i1_n_pmosrow = self.polyrow(
1804
+ i1_pmosrow0 = self.polyrow(
1821
1805
  name="i1_n_pmosrow", elements=(i1_n_pmospad, i1_n_pmos),
1822
1806
  )
1823
1807
  i1_pass0 = self.activecolumn(
@@ -1828,20 +1812,19 @@ class _Xor2(_acc.ActiveColumnsCell):
1828
1812
  i1_n_nmospad = self.polypad(
1829
1813
  name="i1_n_nmospad", net=_i1_n, left=(i0_pass0, q_pin),
1830
1814
  )
1831
- i1_n_nmosrow = self.polyrow(
1815
+ i1_nmosrow0 = self.polyrow(
1832
1816
  name="i1_n_nmosrow", elements=(i1_n_nmospad, i1_n_nmos)
1833
1817
  )
1834
1818
  i1_pmos0 = self.pmos(name="i1_pmos0", net=i1, w_size="min")
1835
1819
  i1_pmos0pad = self.polypad(
1836
1820
  name="i1_pmos0pad", net=i1, left=(i0_pass0, q_pin),
1837
1821
  )
1838
- i1_pmos0row = self.polyrow(
1822
+ i1_pmosrow0 = self.polyrow(
1839
1823
  name="i1_pmos0row", elements=(i1_pmos0pad, i1_pmos0),
1840
1824
  )
1841
1825
  i1_pass0 = self.activecolumn(
1842
1826
  name="i1_pass0", connect=False, elements=(i1_n_nmos, i1_pmos0),
1843
1827
  )
1844
-
1845
1828
  q_nsd = self.signal_nsd(name="q_nsd", net=q, with_contact=True)
1846
1829
  q_psd = self.signal_psd(name="q_psd", net=q, with_contact=False)
1847
1830
  q_nsdrow = self.m1row(name="q_nsdrow", elements=(q_m1knot0, q_nsd))
@@ -1868,6 +1851,8 @@ class _Xor2(_acc.ActiveColumnsCell):
1868
1851
  i1_n_pad0 = self.polypad(name="i1_n_pad0", net=_i1_n, left=i0_n_pass)
1869
1852
  i1_n_m1knot = self.m1knot(name="i1_n_m1knot", net=_i1_n)
1870
1853
  if not self.inverted:
1854
+ assert i1_n_pmospad is not None, "Internal error"
1855
+
1871
1856
  i1_n_nmos = self.nmos(name="i1_n_nmos", net=_i1_n, w_size="min")
1872
1857
  i1_n_m1row = self.m1row(
1873
1858
  name="i1_n_m1row", elements=(i1_n_pmospad, i1_n_m1knot),
@@ -1880,6 +1865,8 @@ class _Xor2(_acc.ActiveColumnsCell):
1880
1865
  name="i1_pass1", connect=False, elements=(i1_n_nmos, i1_pmos0),
1881
1866
  )
1882
1867
  else:
1868
+ assert i1_n_nmospad is not None, "Internal error"
1869
+
1883
1870
  i1_nmos0 = self.nmos(name="i1_nmos0", net=i1, w_size="min")
1884
1871
  i1_nmospad = self.polypad(name="i1_nmospad", net=i1)
1885
1872
  i1_n_m1row = self.m1row(
@@ -1902,24 +1889,31 @@ class _Xor2(_acc.ActiveColumnsCell):
1902
1889
  i1_nmos1 = self.nmos(name="i1_nmos1", net=i1, w_size="min")
1903
1890
  i1_pmos1 = self.pmos(name="i1_pmos1", net=i1, w_size="min")
1904
1891
  if not self.inverted:
1892
+ assert i1_pmos0 is not None
1893
+ assert i1_nmos0pad is not None
1894
+
1905
1895
  i1_nmos1pad = self.polypad(name="i1_nmos1pad", net=i1, left=i1_pass1)
1906
1896
  i1_nmos1knot = self.m1knot(name="i1_nmos1knot", net=i1)
1907
- i1_nmos1row = self.polyrow(name="i1_nmos1row", elements=(i1_nmos1pad, i1_nmos1))
1897
+ i1_nmosrow1 = self.polyrow(name="i1_nmos1row", elements=(i1_nmos1pad, i1_nmos1))
1908
1898
  i1_m1row = self.m1row(
1909
1899
  name="i1_m1row", elements=(i1_nmos0pad, i1_nmos1knot),
1910
1900
  )
1911
1901
  i1_pmospad = self.polypad(name="i1_pmospad", net=i1)
1912
- i1_pmosrow = self.polyrow(
1902
+ i1_pmosrow1 = self.polyrow(
1913
1903
  name="i1_pmosrow", elements=(i1_pmos0, i1_pmospad, i1_pmos1),
1914
1904
  )
1915
1905
  i1_pin = self.m1pin(name="i1_pin", elements=(i1_nmos1pad, i1_nmos1knot, i1_pmospad))
1916
1906
  else:
1917
- i1_nmosrow = self.polyrow(
1907
+ assert i1_nmos0 is not None
1908
+ assert i1_nmospad is not None
1909
+ assert i1_pmos0pad is not None
1910
+
1911
+ i1_nmosrow1 = self.polyrow(
1918
1912
  name="i1_nmosrow", elements=(i1_nmos0, i1_nmospad, i1_nmos1),
1919
1913
  )
1920
1914
  i1_pmos1pad = self.polypad(name="i1_pmos1pad", net=i1, left=i1_pass1)
1921
1915
  i1_pmos1knot = self.m1knot(name="i1_pmos1knot", net=i1)
1922
- i1_pmos1row = self.polyrow(name="i1_pmos1row", elements=(i1_pmos1pad, i1_pmos1))
1916
+ i1_pmosrow1 = self.polyrow(name="i1_pmos1row", elements=(i1_pmos1pad, i1_pmos1))
1923
1917
  i1_m1row = self.m1row(
1924
1918
  name="i1_m1row", elements=(i1_pmos0pad, i1_pmos1knot),
1925
1919
  )
@@ -1929,15 +1923,14 @@ class _Xor2(_acc.ActiveColumnsCell):
1929
1923
  name="i1_inv", connect=False, elements=(i1_nmos1, i1_pmos1),
1930
1924
  )
1931
1925
 
1926
+
1932
1927
  i1_n_nsd = self.signal_nsd(name="i1_n_nsd", net=_i1_n, with_contact=True)
1933
1928
  i1_n_psd = self.signal_psd(name="i1_n_psd", net=_i1_n, with_contact=True)
1934
1929
  i1_n_pad = self.polypad(name="i1_n_pad", net=_i1_n)
1930
+ mos = i1_n_nmos if not self.inverted else i1_n_pmos
1931
+ assert mos is not None
1935
1932
  i1_n_polyrow = self.polyrow(
1936
- name="i1_n_polyrow", elements=(
1937
- i1_n_pad0,
1938
- i1_n_nmos if not self.inverted else i1_n_pmos,
1939
- i1_n_pad,
1940
- ),
1933
+ name="i1_n_polyrow", elements=(i1_n_pad0, mos, i1_n_pad),
1941
1934
  )
1942
1935
  i1_n_m1col1 = self.m1column(
1943
1936
  name="i1_n_m1col1", elements=(i1_n_nsd, i1_n_pad, i1_n_psd),
@@ -1955,18 +1948,15 @@ class _Xor2(_acc.ActiveColumnsCell):
1955
1948
  i1_inv, i1_n_sds,
1956
1949
  ),
1957
1950
  polyrows=(
1958
- self.multipolyrow(name="nmos_multirow", rows=(
1959
- i0_nmosrow,
1960
- i1_nmos0row if not self.inverted else i1_n_nmosrow,
1961
- i1_nmos1row if not self.inverted else i1_nmosrow,
1962
- )),
1951
+ self.multipolyrow(
1952
+ name="nmos_multirow", rows=(i0_nmosrow, i1_nmosrow0, i1_nmosrow1),
1953
+ ),
1963
1954
  self.multipolyrow(name="conn_multirow", rows=(
1964
1955
  i0_n_polyrow, i1_n_polyrow,
1965
1956
  )),
1966
- i1_n_pmosrow.multipolyrow if not self.inverted else i1_pmos0row.multipolyrow,
1957
+ i1_pmosrow0.multipolyrow,
1967
1958
  self.multipolyrow(name="pmos_multirow", rows=(
1968
- i0_pmosrow,
1969
- i1_pmosrow if not self.inverted else i1_pmos1row,
1959
+ i0_pmosrow, i1_pmosrow1,
1970
1960
  )),
1971
1961
  ),
1972
1962
  m1rows=(
@@ -3171,6 +3161,93 @@ class _DFFnR(_acc.ActiveColumnsCell):
3171
3161
  )
3172
3162
 
3173
3163
 
3164
+ class _Gallery(_fab.FactoryCell["StdCellFactory"]):
3165
+ def __init__(self, *, name: str, fab: "StdCellFactory", cells: Iterable[_cell.Cell]):
3166
+ cells = tuple(cells)
3167
+
3168
+ canvas = fab.canvas
3169
+
3170
+ super().__init__(name=name, fab=fab)
3171
+
3172
+ ckt = self.new_circuit()
3173
+ layouter = self.new_circuitlayouter()
3174
+
3175
+ l = len(cells)
3176
+
3177
+ cells2 = tuple(reversed(cells))
3178
+
3179
+ # Make a shuffled row of cells for more DRC checking
3180
+ def shuffled_i(i: int) -> int:
3181
+ is_odd = (i%2) == 1
3182
+ if not is_odd:
3183
+ return l - (i//2) - 1
3184
+ else:
3185
+ return (i//2)
3186
+ cells3 = tuple(cells[shuffled_i(i)] for i in range(l))
3187
+
3188
+ vss_ports = []
3189
+ vdd_ports = []
3190
+ x0 = x1 = x2 = 0.0
3191
+ for i, cell in enumerate(cells):
3192
+ inst = ckt.instantiate(cell, name=f"{cell.name}[0]")
3193
+
3194
+ for port in inst.ports:
3195
+ if port.name == "vss":
3196
+ vss_ports.append(port)
3197
+ elif port.name == "vdd":
3198
+ vdd_ports.append(port)
3199
+ else:
3200
+ ckt.new_net(
3201
+ name=f"{inst.name}.{port,name}", external=False, childports=port,
3202
+ )
3203
+
3204
+ layouter.place(inst, x=x0, y=0.0)
3205
+ x0 += cast(_Cell, cell).width
3206
+
3207
+ cell2 = cells2[i]
3208
+ inst = ckt.instantiate(cell2, name=f"{cell2.name}[1]")
3209
+
3210
+ for port in inst.ports:
3211
+ if port.name == "vss":
3212
+ vss_ports.append(port)
3213
+ elif port.name == "vdd":
3214
+ vdd_ports.append(port)
3215
+ else:
3216
+ ckt.new_net(
3217
+ name=f"{inst.name}.{port,name}", external=False, childports=port,
3218
+ )
3219
+
3220
+ layouter.place(inst, x=x1, y=0.0, rotation=_geo.Rotation.MX)
3221
+ x1 += cast(_Cell, cell2).width
3222
+
3223
+ cell3 = cells3[i]
3224
+ inst = ckt.instantiate(cell3, name=f"{cell3.name}[2]")
3225
+
3226
+ for port in inst.ports:
3227
+ if port.name == "vss":
3228
+ vss_ports.append(port)
3229
+ elif port.name == "vdd":
3230
+ vdd_ports.append(port)
3231
+ else:
3232
+ ckt.new_net(
3233
+ name=f"{inst.name}.{port,name}", external=False, childports=port,
3234
+ )
3235
+
3236
+ layouter.place(inst, x=x2, y=2*canvas._cell_height, rotation=_geo.Rotation.MX)
3237
+ x2 += cast(_Cell, cell3).width
3238
+
3239
+ ckt.new_net(name="vss", external=True, childports=vss_ports)
3240
+ ckt.new_net(name="vdd", external=True, childports=vdd_ports)
3241
+
3242
+ # Set boundary
3243
+ assert abs(x0 - x1) < _geo.epsilon, "Internal error"
3244
+ assert abs(x0 - x2) < _geo.epsilon, "Internal error"
3245
+ layouter.layout.boundary = _geo.Rect(
3246
+ left=0.0, bottom=-canvas._cell_height,
3247
+ right=x0, top=2*canvas._cell_height,
3248
+ )
3249
+
3250
+
3174
3251
  class StdCellFactory(_fab.CellFactory[_Cell]):
3175
3252
  def __init__(self, *,
3176
3253
  lib: _lbry.RoutingGaugeLibrary, cktfab: _ckt.CircuitFactory,
@@ -3208,6 +3285,7 @@ class StdCellFactory(_fab.CellFactory[_Cell]):
3208
3285
  self.add_xors()
3209
3286
  self.add_latches()
3210
3287
  self.add_flops()
3288
+ self.add_gallery()
3211
3289
 
3212
3290
  def add_fillers(self):
3213
3291
  """Add a set of fill cells to the library.
@@ -3301,3 +3379,6 @@ class StdCellFactory(_fab.CellFactory[_Cell]):
3301
3379
  def add_flops(self):
3302
3380
  self.new_cell(name="dff_x1", cell_class=_DFF, drive=1)
3303
3381
  self.new_cell(name="dffnr_x1", cell_class=_DFFnR, drive=1)
3382
+
3383
+ def add_gallery(self):
3384
+ self.new_cell(name="Gallery", cell_class=_Gallery, cells=self.lib.cells)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
- Name: c4m-flexcell
3
- Version: 0.4.0.dev23
2
+ Name: c4m_flexcell
3
+ Version: 0.4.2
4
4
  Summary: PDKMaster based scalable standard cell library
5
5
  Author: Staf Verhaegen
6
6
  Author-email: staf@fibraservi.eu
@@ -10,6 +10,9 @@ Project-URL: Bug Tracker, https://gitlab.com/Chips4Makers/c4m-flexcell/issues
10
10
  Requires-Python: ~=3.6
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE.md
13
+ Requires-Dist: setuptools
14
+ Requires-Dist: nest-asyncio
15
+ Requires-Dist: PDKMaster~=0.10.0
13
16
 
14
17
  # Flexible, scalable, standard cell library
15
18
 
@@ -19,9 +22,13 @@ Standard cells are introduced into an [ASIC](https://en.wikipedia.org/wiki/Appli
19
22
 
20
23
  ## Release History
21
24
 
22
- Current development is done on a development branch for version 0.4. Documentation/README will
23
- be updated together with the release of this version.
24
-
25
+ * [v0.4.2](https://gitlab.com/Chips4Makers/c4m-flexcell/-/commits/v0.10.2): only internal
26
+ improvements
27
+ * v0.4.1:
28
+ major reworking of the code. A new class ActiveColumnsCell has been introduced to generate the layouts of the standard cells. This removes the lambda based dimensions from the cells to compute them all from the design rules.
29
+ All cells were converter to this new layout method and this should allow to make standard cell libraries with much lower height of the cells. Compatiblity function are provided so that layout spec can be derived as was done previously as based on a lambda value.
30
+ v0.4.0 had only dev releases and code was rebased and squashed for this release.
31
+ * v0.3.3: bug fix; remove public 0.3.2 release
25
32
  * v0.3.2: code cleansing, bug fixing, update dependencies
26
33
  * v0.3.1: small update for Coriolis export
27
34
  * v0.3.0: Update for [release v0.9.0 of PDKMaster](https://gitlab.com/Chips4Makers/PDKMaster/-/blob/v0.9.0/ReleaseNotes/v0.9.0.md); replace Library-> StdCellFactory to follow common usage of a factory to generate cells.
@@ -39,7 +46,5 @@ In future options are planned so libraries can be generated for different target
39
46
  ## Status
40
47
 
41
48
  This repository is currently considered experimental code with no backwards compatibility guarantees whatsoever.
42
- This development is done in a development branch for new layout generation code.
43
- When this development is done it will be released as version 0.4.0.
44
49
  Current implementation is based on the topology of the Coriolis nsxlib standard cells with some area improvements but not yet with optimal area use. For v0.1 of this library a total replacement of the layout generation is planned fully based on minimized area for the technology design rules.
45
50
  If interested head over to [gitter](https://gitter.im/Chips4Makers/community) for further discussion.
@@ -0,0 +1,3 @@
1
+ setuptools
2
+ nest-asyncio
3
+ PDKMaster~=0.10.0
@@ -5,3 +5,4 @@ sphinx
5
5
  build
6
6
  setuptools
7
7
  setuptools_scm
8
+ nest-asyncio
@@ -86,7 +86,7 @@ def task_install():
86
86
 
87
87
  return {
88
88
  "title": lambda _: "Installing python module",
89
- "file_dep": c4m_py_files,
89
+ "file_dep": (top_dir.joinpath("setup.py"), *c4m_py_files),
90
90
  "targets": (flexcell_inst_dir,),
91
91
  "actions": (
92
92
  f"{pip} install --no-deps {top_dir}",
@@ -5,7 +5,7 @@ def local_scheme(version):
5
5
  if version.tag and not version.distance:
6
6
  return version.format_with("")
7
7
  else:
8
- return version.format_choice("", "+{node}.dirty")
8
+ return version.format_choice("+{node}", "+{node}.dirty")
9
9
 
10
10
  # Don't seem to have much success with exclude parameter of find_packages()
11
11
  _packages = list(filter(lambda pkg: pkg[:4] != "test", find_packages()))
@@ -28,7 +28,7 @@ setup(
28
28
  license="GPL-2.0-or-later OR AGPL-3.0-or-later OR CERN-OHL-S-2.0+",
29
29
  python_requires="~=3.6",
30
30
  setup_requires=["setuptools_scm"],
31
- install_requires=["setuptools", "PDKMaster~=0.9.4"],
31
+ install_requires=["setuptools", "nest-asyncio", "PDKMaster~=0.10.0"],
32
32
  include_package_data=True,
33
33
  packages=_packages,
34
34
  project_urls={
@@ -1,2 +0,0 @@
1
- setuptools
2
- PDKMaster~=0.9.4