| #!/usr/bin/env python3 | |
| import lxml.etree as ET | |
| from enum import Enum | |
| def clog2(x): | |
| """Ceiling log 2 of x. | |
| >>> clog2(0), clog2(1), clog2(2), clog2(3), clog2(4) | |
| (0, 0, 1, 2, 2) | |
| >>> clog2(5), clog2(6), clog2(7), clog2(8), clog2(9) | |
| (3, 3, 3, 3, 4) | |
| >>> clog2(1 << 31) | |
| 31 | |
| >>> clog2(1 << 63) | |
| 63 | |
| >>> clog2(1 << 11) | |
| 11 | |
| """ | |
| x -= 1 | |
| i = 0 | |
| while True: | |
| if x <= 0: | |
| break | |
| x = x >> 1 | |
| i += 1 | |
| return i | |
| def add_metadata(tag, mtype, msubtype): | |
| meta_root = ET.SubElement(tag, 'metadata') | |
| meta_type = ET.SubElement(meta_root, 'meta', {'name': 'type'}) | |
| meta_type.text = mtype | |
| meta_subtype = ET.SubElement(meta_root, 'meta', {'name': 'subtype'}) | |
| meta_subtype.text = msubtype | |
| return meta_root | |
| class MuxType(Enum): | |
| LOGIC = 'BEL_MX' | |
| ROUTING = 'BEL_RX' | |
| class MuxPinType(Enum): | |
| INPUT = 'i' | |
| SELECT = 's' | |
| OUTPUT = 'o' | |
| def verilog(self): | |
| if self in (self.INPUT, self.SELECT): | |
| return "input wire" | |
| elif self == self.OUTPUT: | |
| return "output wire" | |
| else: | |
| raise TypeError( | |
| "Can't convert {} into verilog definition.".format(self) | |
| ) | |
| def direction(self): | |
| if self in (self.INPUT, self.SELECT): | |
| return "input" | |
| elif self == self.OUTPUT: | |
| return "output" | |
| else: | |
| raise TypeError( | |
| "Can't convert {} into verilog definition.".format(self) | |
| ) | |
| def __str__(self): | |
| return self.value | |
| class ModulePort(object): | |
| def __init__(self, pin_type, name, width, index, data_width=1): | |
| self.name = name | |
| self.pin_type = pin_type | |
| self.width = width | |
| self.index = index | |
| self.data_width = data_width | |
| def getDefinition(self): | |
| if self.width == 1: | |
| if self.data_width is not None and self.data_width > 1: | |
| return '\t%s [%d:0] %s;\n' % ( | |
| self.pin_type.verilog(), self.data_width - 1, self.name | |
| ) | |
| else: | |
| return '\t%s %s;\n' % (self.pin_type.verilog(), self.name) | |
| else: | |
| return '\t%s %s %s;\n' % ( | |
| self.pin_type.verilog(), self.index, self.name | |
| ) | |
| def pb_type_xml(mux_type, mux_name, pins, subckt=None, num_pb=1, comment=""): | |
| """Generate <pb_type> XML for a mux. | |
| Parameters | |
| ---------- | |
| mux_type: MuxType | |
| Type of mux to create. | |
| mux_name: str | |
| Name of the mux. | |
| pins: [(MuxPinType, str, int, int),] | |
| List of tuples which contain (pin type, pin name, port width, index) | |
| subckt: str | |
| Name of the blif_model for the mux. Only valid when mux_type == | |
| MuxType.LOGIC. | |
| num_pb: int | |
| Value for the num_pb value. Defaults to 1. | |
| comment: str | |
| Optional comment for the mux. | |
| Returns | |
| ------- | |
| xml.etree.ElementTree | |
| pb_type.xml for requested mux | |
| """ | |
| assert isinstance(comment, | |
| str), "{} {}".format(type(comment), repr(comment)) | |
| if mux_type not in (MuxType.LOGIC, MuxType.ROUTING): | |
| assert False, "Unknown type {}".format(mux_type) | |
| pb_type_xml = ET.Element( | |
| 'pb_type', { | |
| 'name': mux_name, | |
| 'num_pb': str(num_pb), | |
| } | |
| ) | |
| if mux_type == MuxType.LOGIC: | |
| add_metadata(pb_type_xml, 'bel', 'mux') | |
| else: | |
| add_metadata(pb_type_xml, 'bel', 'routing') | |
| if mux_type == MuxType.LOGIC: | |
| model = ET.SubElement(pb_type_xml, "blif_model") | |
| model.text = '.subckt {}'.format(subckt) | |
| else: | |
| assert not subckt, "Provided subckt={} for non-logic mux!".format( | |
| subckt | |
| ) | |
| if comment is not None: | |
| pb_type_xml.append(ET.Comment(comment)) | |
| for port in pins: | |
| # assert port.index < port.width, ( | |
| # "Pin index {} >= width {} for pin {} {}".format( | |
| # port.index, port.width, port.name, port.pin_type | |
| # ) | |
| # ) | |
| if mux_type == MuxType.ROUTING and port.pin_type == MuxPinType.SELECT: | |
| continue | |
| assert port.width == 1 or port.data_width == 1, ( | |
| 'Only one of width(%d) or data_width(%d) may > 1 for pin %s' % | |
| (port.width, port.data_width, port.name) | |
| ) | |
| if port.width == 1 and port.data_width > 1: | |
| num_pins = port.data_width | |
| else: | |
| num_pins = port.width | |
| mux = ET.SubElement( | |
| pb_type_xml, | |
| port.pin_type.direction(), | |
| { | |
| 'name': port.name, | |
| 'num_pins': str(num_pins) | |
| }, | |
| ) | |
| if mux_type == MuxType.LOGIC: | |
| for inport in pins: | |
| if inport.pin_type not in (MuxPinType.INPUT, MuxPinType.SELECT): | |
| continue | |
| for outport in pins: | |
| if outport.pin_type not in (MuxPinType.OUTPUT, ): | |
| continue | |
| if inport.name.startswith('I'): | |
| delay_inport = inport.name[1] | |
| else: | |
| # if it is not IX it must be S | |
| delay_inport = "S0" | |
| # XXX: temporary workaroud | |
| if mux_name == "F6MUX": | |
| maxdel = "10e-12" | |
| else: | |
| maxdel = "{{iopath_{}_OUT}}".format(delay_inport) | |
| ET.SubElement( | |
| pb_type_xml, | |
| 'delay_constant', | |
| { | |
| 'max': maxdel, | |
| 'in_port': "%s" % inport.name, | |
| 'out_port': "%s" % outport.name, | |
| }, | |
| ) | |
| elif mux_type == MuxType.ROUTING: | |
| interconnect = ET.SubElement(pb_type_xml, 'interconnect') | |
| inputs = [ | |
| "{}.{}".format(mux_name, port.name) | |
| for port in pins | |
| if port.pin_type in (MuxPinType.INPUT, ) | |
| ] | |
| outputs = [ | |
| "{}.{}".format(mux_name, port.name) | |
| for port in pins | |
| if port.pin_type in (MuxPinType.OUTPUT, ) | |
| ] | |
| assert len(outputs) == 1 | |
| mux = ET.SubElement( | |
| interconnect, | |
| 'mux', | |
| { | |
| 'name': '%s' % mux_name, | |
| 'input': " ".join(inputs), | |
| 'output': outputs[0], | |
| }, | |
| ) | |
| meta_root = add_metadata(mux, 'bel', 'routing') | |
| meta_fasm_mux = ET.SubElement(meta_root, 'meta', {'key': 'fasm_mux'}) | |
| meta_fasm_mux.text = "\n".join( | |
| [""] + ["{0} = {0}".format(i) for i in inputs] + [""] | |
| ) | |
| return pb_type_xml | |
| if __name__ == "__main__": | |
| import doctest | |
| failure_count, test_count = doctest.testmod() | |
| assert test_count > 0 | |
| assert failure_count == 0, "Doctests failed!" |