blob: a4c8f0953ff97f69883fbe52202a86d4291bbe93 [file] [log] [blame]
#!/usr/bin/env python3
"""Functions for working with pb_type.xml files."""
from typing import Dict, Tuple, List, Union
from io import StringIO
import lxml.etree as ET
PBTypeName = str
PortName = str
PortWidth = int
SinglePinPort = PortName
MultiPinPort = Tuple[PortName, PortWidth]
Port = Union[SinglePinPort, MultiPinPort]
ClockPort = Port
InputPort = Port
OutputPort = Port
CarryName = str
def xps(s):
"""XML Parse String (internal helper function)."""
return ET.parse(StringIO(s)).getroot()
def get_blif_model(pbtype_tag: ET.Element) -> str:
"""Get the blif_model string.
Supports both `blif_model` attribute and `<blif_model>` tag.
>>> print(get_blif_model(xps('''<pb_type />''')))
None
>>> get_blif_model(xps('''<pb_type blif_model="hello"/>'''))
'hello'
>>> get_blif_model(xps('''\\
... <pb_type><blif_model>hello</blif_model></pb_type>
... '''))
'hello'
"""
model = ""
blif_model = pbtype_tag.find("blif_model")
if blif_model is not None:
model = blif_model.text
if 'blif_model' in pbtype_tag.attrib:
model = pbtype_tag.attrib['blif_model']
model = model.strip()
if model:
return model
def find_leaf(root: ET.Element) -> ET.Element:
"""Find first leaf pb_type tag (otherwise None)."""
def all_pbtype_tags(root):
if root.tag == "pb_type":
yield root
yield from root.findall(".//pb_type")
for pbtype_tag in all_pbtype_tags(root):
if get_blif_model(pbtype_tag):
return pbtype_tag
def ports(
pbtype_tag: ET.Element, assert_leaf=False
) -> Tuple[PBTypeName, List[ClockPort], List[InputPort], List[OutputPort],
Dict[CarryName, Tuple[InputPort, OutputPort]]]:
"""Get the clock, input and output pins from a pb_type.
Returns
-------
([("clock_name", width), ...],
[("input_name", width), ...],
[("output_name", width), ...],
{"carry_name": ("input port", "output_port"), ...})
"""
assert pbtype_tag.tag == "pb_type", "Unknown tag {}\n{}".format(
pbtype_tag.tag, ET.dump(pbtype_tag)
)
pbtype_name = pbtype_tag.attrib['name']
clocks = []
for clock_tag in pbtype_tag.findall("clock"):
clocks.append(
(clock_tag.attrib['name'], int(clock_tag.attrib['num_pins']))
)
carry = {}
def set_carry(name, in_port=None, out_port=None):
"""Add given in_port and/or out_port to carry dict
"""
if name not in carry:
carry[name] = [None, None]
if in_port is not None:
assert carry[name][0] is None, (
"Can't set {}: Carry {} in port already {}".format(
in_port, name, carry[name][0]
)
)
carry[name][0] = in_port
if out_port is not None:
assert carry[name][
-1
] is None, "Can't set {}: Carry {} out port already {}".format(
out_port, name, carry[name][-1]
)
carry[name][-1] = out_port
inputs = []
for input_tag in pbtype_tag.findall("input"):
name = input_tag.attrib['name']
num_pins = int(input_tag.attrib['num_pins'])
carry_tag = input_tag.find("pack_pattern[@type='carry']")
if carry_tag is not None:
assert carry_tag.attrib['type'] == 'carry'
assert num_pins == 1
set_carry(carry_tag.attrib['name'], in_port=name)
continue
inputs.append((name, num_pins))
outputs = []
for output_tag in pbtype_tag.findall("output"):
name = output_tag.attrib['name']
num_pins = int(output_tag.attrib['num_pins'])
carry_tag = output_tag.find("pack_pattern[@type='carry']")
if carry_tag is not None:
assert carry_tag.attrib['type'] == 'carry'
assert num_pins == 1
set_carry(carry_tag.attrib['name'], out_port=name)
continue
outputs.append(
(output_tag.attrib['name'], int(output_tag.attrib['num_pins']))
)
return pbtype_name, clocks, inputs, outputs, {
k: tuple(v)
for k, v in carry.items()
}
def get_pb_type_chain(node):
pb_types = []
while True:
parent = node.getparent()
if parent is None:
return list(reversed(pb_types))
if parent.tag == 'pb_type':
pb_types.append(parent.attrib['name'])
node = parent