Exemple #1
0
def load_sdf_timings(sdf_dir):
    """
    Loads and merges SDF timing data from all *.sdf files in the given
    directory.
    """

    def apply_scale(cells, scale=1.0):
        """
        Scales all timings represented by the given SDF structure.
        """
        for cell_type, cell_data in cells.items():
            for instance, instance_data in cell_data.items():
                for timing, timing_data in instance_data.items():
                    paths = timing_data["delay_paths"]
                    for path_name, path_data in paths.items():

                        for k in path_data.keys():
                            if path_data[k] is not None:
                                path_data[k] *= scale

    # List SDF files
    files = [f for f in os.listdir(sdf_dir) if f.lower().endswith(".sdf")]

    # Read and parse
    cell_timings = {}

    for f in files:
        print("Loading SDF: '{}'".format(f))

        # Read
        fname = os.path.join(sdf_dir, f)
        with open(fname, "r") as fp:
            sdf = sdfparse.parse(fp.read())

            # Get the timing scale
            header = sdf["header"]
            if "timescale" in header:
                timescale = get_scale_seconds(header["timescale"])
            else:
                print("WARNING: the SDF has no timescale, assuming 1.0")
                timescale = 1.0

            # Apply the scale and update cells
            cells = sdf["cells"]
            apply_scale(cells, timescale)

            cell_timings.update(cells)

    return cell_timings
def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--read_sdf',
                        type=argparse.FileType('r'),
                        help='sdf file to read timing from')
    parser.add_argument('--read_arch_xml',
                        type=argparse.FileType('r'),
                        help='arch xml file to read and update/add timing')
    parser.add_argument(
        '--write_arch_xml',
        type=argparse.FileType('w'),
        help='arch xml file to write with updaed timing calues')

    parser.add_argument('-v', type=bool, help='verbose output')

    logging.basicConfig(level=logging.WARNING)

    args = parser.parse_args()
    timing = sdf_parse(args.read_sdf.read())

    tree = ET.parse(args.read_arch_xml, ET.XMLParser(remove_blank_text=True))

    scale = get_scale_seconds(timing['header']['timescale'])
    # logging.info('sdf scale set to', scale)

    # flatten cells to list of max
    flat_timing = dict()
    for cell_name, cell in timing['cells'].items():
        flat_timing[cell_name] = []
        for instance_key, instance in cell.items():
            assert (
                instance_key == '*'
            ), "For iCE40 expect only wildcard instance {} in cell {}".format(
                instance_key, cell_name)
            for _, path in instance.items():
                flat_timing[cell_name].append(path)

    for key, time_list in flat_timing.items():
        logging.debug(key)
        for delay in time_list:
            logging.debug(delay)

    # look up parsed sdf on in from_pin, to_pin, and type
    def lookup_timing(timing_list, type, to_pin, from_pin):
        ret = []
        for xx in timing_list:
            if type == xx['type'] and xx['to_pin'].startswith(
                    to_pin) and xx['from_pin'].startswith(from_pin):
                ret.append(xx)
            else:
                pass
        return ret

    def get_pessimistic(timing):
        vals = []
        for del_type in timing['delay_paths'].values():
            for dels in del_type.values():
                vals.append(dels)
        max_del = max(vals)
        return str(max_del * scale)

    # TODO: need to take max across negedge and posedge

    # remove all existing tags and warn on them

    # iterate over existing arch and update/add delay tags
    # root = tree.getroot()
    for el in tree.iter('pb_type'):
        pb_name = el.attrib['name']
        # insert timing tags from SDF file
        if pb_name in _arch_to_sdf.keys():
            cell_name, pin_table = _arch_to_sdf.get(pb_name, None)
            for timing in flat_timing[cell_name]:

                def try_translate_pin(table, timing, name):
                    res = [
                        entry for entry in table if entry.sdf == timing[name]
                    ]
                    assert len(res) <= 1

                    if len(res) == 1:
                        return res[0]
                    else:
                        return None

                if timing['type'] == 'hold' or timing['type'] == 'setup':
                    topin = try_translate_pin(pin_table, timing, 'to_pin')
                    frompin = try_translate_pin(pin_table, timing, 'from_pin')
                    if topin is None or frompin is None:
                        continue
                    attribs = {
                        'clock': frompin.arch,
                        'port': '{}.{}'.format(pb_name, topin.arch),
                        'value': get_pessimistic(timing)
                    }
                    hold_setup_el = ET.SubElement(
                        el, 'T_{}'.format(timing['type']), attribs)
                    # hold_setup_el.tail = '\n'
                    logging.info(ET.tostring(hold_setup_el))
                elif timing['type'] == 'iopath':
                    topin = try_translate_pin(pin_table, timing, 'to_pin')
                    frompin = try_translate_pin(pin_table, timing, 'from_pin')
                    if topin is None or frompin is None:
                        continue
                    if frompin.is_clk:
                        attribs = {
                            'clock': '{}'.format(frompin.arch),
                            'port': '{}.{}'.format(pb_name, topin.arch),
                            'max': get_pessimistic(timing)
                        }
                        iopath_el = ET.SubElement(el, 'T_clock_to_Q', attribs)
                        # iopath_el.tail = '\n'
                    else:
                        attribs = {
                            'in_port': '{}.{}'.format(pb_name, frompin.arch),
                            'out_port': '{}.{}'.format(pb_name, topin.arch),
                            'max': get_pessimistic(timing)
                        }
                        iopath_el = ET.SubElement(el, 'delay_constant',
                                                  attribs)
                        # iopath_el.tail = '\n'
                    logging.info(ET.tostring(iopath_el))
                elif timing['type'] == 'recovery':
                    pass
                elif timing['type'] == 'removal':
                    pass

    xml_str = ET.tostring(tree, pretty_print=True).decode('utf-8')
    args.write_arch_xml.write(xml_str)