blob: 1b0807886d011181f2d293744b9f69ac7c2aeacd [file] [log] [blame]
#!/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 re
import os
from enum import Flag
from dataclasses import dataclass
from typing import List, Optional
from . import base
CornerTypeMappings = {}
# "wo" is "worst-case one" and corresponds to "fs"
CornerTypeMappings["wo"] = "fs"
# "wz" is "worst-case zero" and corresponds to "sf"
CornerTypeMappings["wz"] = "sf"
# "wp" is "worst-case power" and corresponds to "ff"
CornerTypeMappings["wp"] = "ff"
# "ws" is "worst-case speed" and corresponds to "ss"
CornerTypeMappings["ws"] = "ss"
class CornerType(Flag):
"""
>>> CornerType.parse('t')
CornerType.t
>>> CornerType.parse('tt')
[CornerType.t, CornerType.t]
>>> CornerType.parse('wp')
[CornerType.f, CornerType.s]
"""
t = 'Typical' # all nominal (typical) values
f = 'Fast' # fast, that is, values that make transistors run faster
s = 'Slow' # slow
@classmethod
def parse(cls, s):
if s in CornerTypeMappings:
return cls.parse(CornerTypeMappings[s])
if len(s) > 1:
try:
o = []
for c in s:
o.append(cls.parse(c))
return o
except TypeError:
raise TypeError("Unknown corner type: {}".format(s))
if not hasattr(cls, s):
raise TypeError("Unknown corner type: {}".format(s))
return getattr(cls, s)
def __repr__(self):
return 'CornerType.'+self.name
def __str__(self):
return self.value
class CornerFlag(Flag):
nointpr = 'No internal power'
lv = 'Low voltage'
ccsnoise = 'Composite Current Source Noise'
ns5 = '5 nanoseconds'
pwr = 'Power'
@classmethod
def parse(cls, s):
if s == "5ns":
return cls.ns5
elif hasattr(cls, s):
return getattr(cls, s)
else:
raise TypeError("Unknown CornerFlags: {}".format(s))
def __repr__(self):
return 'CornerType.'+self.name
def __str__(self):
return self.value
@dataclass
class Corner:
volts: List[float]
temps: List[int]
flags: List[CornerFlag]
types: Optional[List[CornerType]] = None
VOLTS_REGEX = re.compile('([0-9]p[0-9]+)V')
TEMP_REGEX = re.compile('(n?)([0-9][0-9]+)C')
def parse_filename(pathname):
"""Extract corner information from a filename.
>>> parse_filename('tt_1p80V_3p30V_3p30V_25C')
Corner(volts=[1.8, 3.3, 3.3], temps=[25], flags=[], types=[CornerType.t, CornerType.t])
>>> parse_filename('sky130_fd_io__top_ground_padonlyv2__tt_1p80V_3p30V_3p30V_25C.wrap.lib')
Corner(volts=[1.8, 3.3, 3.3], temps=[25], flags=[], types=[CornerType.t, CornerType.t])
>>> parse_filename('sky130_fd_sc_ms__tt_1p80V_100C.wrap.json')
Corner(volts=[1.8], temps=[100], flags=[], types=[CornerType.t, CornerType.t])
>>> parse_filename('sky130_fd_sc_ms__tt_1p80V_100C.wrap.lib')
Corner(volts=[1.8], temps=[100], flags=[], types=[CornerType.t, CornerType.t])
>>> parse_filename('sky130_fd_sc_ms__tt_1p80V_25C_ccsnoise.wrap.json')
Corner(volts=[1.8], temps=[25], flags=[CornerType.ccsnoise], types=[CornerType.t, CornerType.t])
>>> parse_filename('sky130_fd_sc_ms__wp_1p56V_n40C_5ns.wrap.json')
Corner(volts=[1.56], temps=[-40], flags=[CornerType.ns5], types=[CornerType.f, CornerType.s])
>>> parse_filename('sky130_fd_sc_ms__wp_1p65V_n40C.wrap.json')
Corner(volts=[1.65], temps=[-40], flags=[], types=[CornerType.f, CornerType.s])
>>> parse_filename('sky130_fd_sc_ms__wp_1p95V_85C_pwr.wrap.lib')
Corner(volts=[1.95], temps=[85], flags=[CornerType.pwr], types=[CornerType.f, CornerType.s])
>>> parse_filename('sky130_fd_sc_ms__wp_1p95V_n40C_ccsnoise.wrap.json')
Corner(volts=[1.95], temps=[-40], flags=[CornerType.ccsnoise], types=[CornerType.f, CornerType.s])
>>> parse_filename('sky130_fd_sc_ms__wp_1p95V_n40C_pwr.wrap.lib')
Corner(volts=[1.95], temps=[-40], flags=[CornerType.pwr], types=[CornerType.f, CornerType.s])
"""
pathname = pathname.replace('-', base.SEPERATOR) # FIXME: !!!
if base.SEPERATOR in pathname:
_, extra, extension = base.parse_filename(pathname)
else:
extra = pathname
extension = ''
if extension not in ('', 'lib', 'wrap.lib', 'wrap.json'):
raise ValueError('Not possible to extract corners from: {!r}'.format(extension))
if not extra:
raise ValueError('No corners found in: {!r}'.format(pathname))
kw = {}
kw['flags'] = []
kw['volts'] = []
kw['temps'] = []
bits = extra.split("_")
while len(bits) > 0:
b = bits.pop(0)
if VOLTS_REGEX.match(b):
assert b.endswith('V'), b
kw['volts'].append(float(b[:-1].replace('p', '.')))
elif TEMP_REGEX.match(b):
assert b.endswith('C'), b
kw['temps'].append(int(b[:-1].replace('n', '-')))
else:
if 'types' not in kw:
kw['types'] = CornerType.parse(b)
else:
kw['flags'].append(CornerFlag.parse(b))
return Corner(**kw)
# 1p60V 5p50V n40C
if __name__ == "__main__":
import doctest
doctest.testmod()