| #!/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 |