| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2020 The SkyWater PDK Authors. |
| # |
| # Use of this source code is governed by the Apache 2.0 |
| # license that can be found in the LICENSE file or at |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import os |
| |
| from dataclasses import dataclass |
| from enum import Enum |
| from typing import Optional |
| |
| |
| def parse_filename(pathname): |
| """Extract library and module name from pathname. |
| |
| >>> t = list(parse_filename('sky130_fd_io-top_ground_padonlyv2-tt_1p80V_3p30V_3p30V_25C.wrap.lib')) |
| >>> t.pop(0) |
| Cell(name='top_ground_padonlyv2', library=Library(node=LibraryNode.SKY130, source=LibrarySource('fd'), type=LibraryType.io, name=None)) |
| >>> t.pop(0) |
| 'tt_1p80V_3p30V_3p30V_25C' |
| >>> t.pop(0) |
| 'wrap.lib' |
| |
| """ |
| dirname, filename = os.path.split(pathname) |
| |
| if '.' in filename: |
| basename, extension = filename.split('.', 1) |
| else: |
| basename = filename |
| extension = '' |
| basename = basename.replace('-', SEPERATOR) |
| |
| library, cell, extra = basename.split(SEPERATOR, 3) |
| return (Cell.parse(library+SEPERATOR+cell), extra, extension) |
| |
| |
| SEPERATOR = "__" |
| |
| |
| class LibraryNode(Enum): |
| SKY130 = "SkyWater 130nm" |
| |
| @classmethod |
| def parse(cls, s): |
| s = s.upper() |
| if not hasattr(cls, s): |
| raise ValueError("Unknown node: {}".format(s)) |
| return getattr(cls, s) |
| |
| def __repr__(self): |
| return "LibraryNode."+self.name |
| |
| |
| class LibrarySource(str): |
| """Where a library was created.""" |
| Known = [] |
| |
| @classmethod |
| def parse(cls, s): |
| try: |
| return cls.Known[cls.Known.index(s)] |
| except ValueError: |
| return cls(s) |
| |
| @property |
| def fullname(self): |
| if self in self.Known: |
| return self.__doc__ |
| else: |
| return 'Unknown source: '+str.__repr__(self) |
| |
| def __repr__(self): |
| return 'LibrarySource({})'.format(str.__repr__(self)) |
| |
| |
| Foundary = LibrarySource("fd") |
| Foundary.__doc__ = "The SkyWater Foundary" |
| LibrarySource.Known.append(Foundary) |
| |
| Efabless = LibrarySource("ef") |
| Efabless.__doc__ = "Efabless" |
| LibrarySource.Known.append(Efabless) |
| |
| OSU = LibrarySource("osu") |
| OSU.__doc__ = "Oklahoma State University" |
| LibrarySource.Known.append(OSU) |
| |
| |
| class LibraryType(Enum): |
| pr = "Primitives" |
| sc = "Standard Cells" |
| sp = "Build Space (Flash, SRAM, etc)" |
| io = "IO and Periphery" |
| xx = "Miscellaneous" |
| |
| @classmethod |
| def parse(cls, s): |
| if not hasattr(cls, s): |
| raise ValueError("Unknown library type: {}".format(s)) |
| return getattr(cls, s) |
| |
| def __repr__(self): |
| return "LibraryType."+self.name |
| |
| def __str__(self): |
| return self.value |
| |
| |
| @dataclass |
| class Library: |
| """ |
| |
| >>> l = Library.parse("sky130_fd_sc_hd") |
| >>> l |
| Library(node=LibraryNode.SKY130, source=LibrarySource('fd'), type=LibraryType.sc, name='hd') |
| >>> l.fullname |
| 'sky130_fd_sc_hd' |
| >>> l.source.fullname |
| 'The SkyWater Foundary' |
| >>> print(l.type) |
| Standard Cells |
| |
| >>> l = Library.parse("sky130_rrr_sc_hd") |
| >>> l |
| Library(node=LibraryNode.SKY130, source=LibrarySource('rrr'), type=LibraryType.sc, name='hd') |
| >>> l.fullname |
| 'sky130_rrr_sc_hd' |
| >>> l.source.fullname |
| "Unknown source: 'rrr'" |
| |
| |
| """ |
| |
| node: LibraryNode |
| source: LibrarySource |
| type: LibraryType |
| name: Optional[str] = None |
| |
| @property |
| def fullname(self): |
| output = [] |
| output.append(self.node.name.lower()) |
| output.append(self.source.lower()) |
| output.append(self.type.name) |
| if self.name: |
| output.append(self.name) |
| return "_".join(output) |
| |
| @classmethod |
| def parse(cls, s): |
| if SEPERATOR in s: |
| raise ValueError( |
| "Found separator '__' in library name: {!r}".format(s)) |
| |
| bits = s.split("_") |
| if len(bits) < 3: |
| raise ValueError( |
| "Did not find enough parts in library name: {}".format(bits)) |
| |
| kw = {} |
| kw['node'] = LibraryNode.parse(bits.pop(0)) |
| kw['source'] = LibrarySource.parse(bits.pop(0)) |
| kw['type'] = LibraryType.parse(bits.pop(0)) |
| if bits: |
| kw['name'] = bits.pop(0) |
| return cls(**kw) |
| |
| |
| |
| @dataclass |
| class Cell: |
| """ |
| >>> c = Cell.parse("sky130_fd_sc_hd__abc") |
| >>> c |
| Cell(name='abc', library=Library(node=LibraryNode.SKY130, source=LibrarySource('fd'), type=LibraryType.sc, name='hd')) |
| >>> c.fullname |
| 'sky130_fd_sc_hd__abc' |
| |
| >>> c = Cell.parse("abc") |
| >>> c |
| Cell(name='abc', library=None) |
| >>> c.fullname |
| Traceback (most recent call last): |
| ... |
| ValueError: Can't get fullname for cell without a library! Cell(name='abc', library=None) |
| """ |
| |
| name: str |
| library: Optional[Library] = None |
| |
| @property |
| def fullname(self): |
| if not self.library: |
| raise ValueError( |
| "Can't get fullname for cell without a library! {}".format( |
| self)) |
| return "{}__{}".format(self.library.fullname, self.name) |
| |
| @classmethod |
| def parse(cls, s): |
| kw = {} |
| if SEPERATOR in s: |
| library, s = s.split(SEPERATOR, 1) |
| kw['library'] = Library.parse(library) |
| kw['name'] = s |
| return cls(**kw) |
| |
| |
| |
| if __name__ == "__main__": |
| import doctest |
| doctest.testmod() |