Compare commits

...

6 Commits

@ -1,4 +1,3 @@
from typing import Dict, List, Tuple, Union, Optional
import datetime
from collections import Counter
from dataclasses import dataclass, field
@ -8,25 +7,25 @@ from itertools import chain
@dataclass
class Device:
BinType: str
NullBin: Union[str, int]
ProductId: Optional[str] = None
LotId: Optional[str] = None
WaferSize: Optional[float] = None
CreateDate: Optional[datetime.datetime] = None
DeviceSizeX: Optional[float] = None
DeviceSizeY: Optional[float] = None
SupplierName: Optional[str] = None
OriginLocation: Optional[int] = None
NullBin: str | int
ProductId: str | None = None
LotId: str | None = None
WaferSize: float | None = None
CreateDate: datetime.datetime | None = None
DeviceSizeX: float | None = None
DeviceSizeY: float | None = None
SupplierName: str | None = None
OriginLocation: int | None = None
MapType: str = 'Array'
Orientation: float = 0
reference_xy: Optional[Tuple[int, int]] = None
reference_xy: tuple[int, int] | None = None
bin_pass: Dict[Union[int, str], bool] = field(default_factory=dict) # Is this bin passing?
map: Union[List[List[int]], List[List[str]]] = field(default_factory=list) # The actual map
# Map attribs: MapName, MapVersion
# SupplierData attribs: ProductCode, RecipeName
bin_pass: dict[int | str, bool] = field(default_factory=dict) # Is this bin passing?
map: list[list[int]] | list[list[str]] = field(default_factory=list) # The actual map
data_misc: dict[str, str] = field(default_factory=dict) # <Data attribs>
supplier_data: dict[str, str] = field(default_factory=dict) # <SupplierData attribs>
misc: Dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
misc: dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
@property
def Rows(self) -> int:
@ -46,9 +45,9 @@ class Device:
class Map:
xmlns: str = 'http://www.semi.org'
FormatRevision: str = "SEMI G85 0703"
SubstrateType: Optional[str] = None
SubstrateId: Optional[str] = None
SubstrateType: str | None = None
SubstrateId: str | None = None
devices: List[Device] = field(default_factory=list)
misc: Dict[str, str] = field(default_factory=dict) # Any unexpected fields go here
devices: list[Device] = field(default_factory=list)
misc: dict[str, str] = field(default_factory=dict) # Any unexpected fields go here

@ -1,4 +1,4 @@
from typing import List, Union, TextIO, Any
from typing import TextIO, Any
import logging
import datetime
from dataclasses import fields
@ -10,7 +10,7 @@ from .main import Map, Device
logger = logging.getLogger(__name__)
def read(stream: TextIO) -> List[Map]:
def read(stream: TextIO) -> list[Map]:
tree = ElementTree.parse(stream)
el_root = tree.getroot()
@ -21,7 +21,7 @@ def read(stream: TextIO) -> List[Map]:
return maps
def read_wmaps(el_root: ElementTree.Element) -> List[Map]:
def read_wmaps(el_root: ElementTree.Element) -> list[Map]:
map_fields = [ff.name for ff in fields(Map)]
maps = []
for el_map in el_root:
@ -41,7 +41,7 @@ def read_wmaps(el_root: ElementTree.Element) -> List[Map]:
return maps
def read_devices(el_map: ElementTree.Element) -> List[Device]:
def read_devices(el_map: ElementTree.Element) -> list[Device]:
dev_fields = [ff.name for ff in fields(Device)]
devices = []
for el_device in el_map:
@ -50,7 +50,7 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
continue
bin_type = el_device.attrib['BinType']
null_bin: Union[int, str]
null_bin: int | str
if bin_type == 'Decimal':
null_bin = int(el_device.attrib['NullBin'])
else:
@ -99,7 +99,7 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
if bin_type == 'Decimal':
bin_code = int(attrib['BinCode'])
else:
bin_code = int(attrib['BinCode'])
bin_code = attrib['BinCode']
if bin_code in device.bin_pass:
logger.error(f'Bin code {bin_code} was repeated; ignoring later entry!')
@ -108,17 +108,23 @@ def read_devices(el_map: ElementTree.Element) -> List[Device]:
device.bin_pass[bin_code] = attrib['BinQuality'].lower() == 'pass'
elif tag == 'Data':
data_strs = [read_row(rr) for rr in el_entry]
data: Union[List[List[str]], List[List[int]]]
data: list[list[str]] | list[list[int]]
if device.BinType == 'Decimal':
data = [[int(vv) for vv in rr] for rr in data_strs]
else:
data = data_strs
device.map = data
for key, value in attrib.items():
device.data_misc[key] = value
elif tag == 'SupplierData':
for key, value in attrib.items():
device.supplier_data[key] = value
devices.append(device)
return devices
def read_row(el_row: ElementTree.Element) -> List[str]:
def read_row(el_row: ElementTree.Element) -> list[str]:
assert _tag(el_row) == 'Row'
row_stripped = (el_row.text or '').strip()

@ -1,4 +1,4 @@
from typing import Sequence, Tuple, List, TextIO, Union
from typing import Sequence, TextIO, cast
import logging
import math
from dataclasses import fields
@ -41,7 +41,7 @@ def write_wmap(wmap: Map, el_root: ElementTree.Element) -> None:
map_fields = [ff.name for ff in fields(wmap)]
for field in map_fields:
if field[0].isupper():
if field[0].isupper() or field == 'xmlns':
val = getattr(wmap, field)
if val is None:
continue
@ -84,8 +84,9 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
el_bin.set('BinQuality', 'Pass' if passed else 'Fail')
el_bin.set('BinCount', str(bin_counts[bin_code]))
el_data = ElementTree.SubElement(el_device, 'Data')
for row_text in row_texts:
el_row = ElementTree.SubElement(el_device, 'Row')
el_row = ElementTree.SubElement(el_data, 'Row')
el_row.text = f'<![CDATA[{row_text}]]>'
# Device attribs
@ -112,27 +113,34 @@ def write_devices(devices: Sequence[Device], el_map: ElementTree.Element) -> Non
continue
el_device.set(key, value)
for key, value in device.data_misc.items():
el_data.set(key, value)
def prepare_data(data: List[List[Union[str, int]]], decimal: bool) -> Tuple[List[str], int]:
if device.supplier_data:
el_suppdata = ElementTree.SubElement(el_device, 'SupplierData')
for key, value in device.data_misc.items():
el_suppdata.set(key, value)
def prepare_data(data: list[list[str]] | list[list[int]], decimal: bool) -> tuple[list[str], int]:
is_char = isinstance(data[0][0], str)
if is_char:
char_len = len(data[0][0])
else:
max_value = max(max(rr) for rr in data)
max_digits = math.ceil(math.log10(max_value))
row_texts = []
for row in data:
if is_char and char_len == 1:
row_text = ''.join(row)
elif is_char:
row_text = ' '.join(row) + ' '
else:
row_text = ' '.join(str(vv).zfill(max_digits) for vv in row) + ' '
row_texts.append(row_text)
if is_char:
data = cast(list[list[str]], data)
char_len = len(data[0][0])
for srow in data:
if char_len == 1:
row_text = ''.join(srow)
else:
row_text = ' '.join(srow) + ' '
row_texts.append(row_text)
return row_texts, char_len
else:
data = cast(list[list[int]], data)
max_value = max(max(rr) for rr in data)
max_digits = math.ceil(math.log10(max_value))
for irow in data:
row_text = ' '.join(str(vv).zfill(max_digits) for vv in irow) + ' '
row_texts.append(row_text)
return row_texts, max_digits

@ -35,7 +35,7 @@ classifiers = [
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
]
requires-python = ">=3.8"
requires-python = ">=3.10"
dynamic = ["version"]
dependencies = [
"numpy~=1.21",

Loading…
Cancel
Save