#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
########################################################################
# File name:       publish_worst_case_paths.py
# Author:          Petr Grillinger
# Description:     Extract worst-case paths from STA report into HTML.
########################################################################

import sys
import re
import os.path

def error(msg, code=-1):
    sys.stderr.write(msg+"\n")
    sys.exit(code)

# parse the STA report file to find worst case path reports 
# this requires that the 'report worst case paths during compilaton' option is enabled
def parse_sta_rpt(rev_name):
    dir,rev = os.path.split(rev_name)
    fw = open(rev + "_worst_case_path_report.html", "w")
    fw.write("<html>\n")
    fw.write("<body>\n")
    fw.write("<h2>Top Failing Paths</h3>\n<hr>\n")
    active = False
    model  = None
    for ln in file(rev_name + '.sta.rpt', "r"):
        s = ln.strip()

        # Timing model
        m = re.match(r"Info: Analyzing (.*) Model", s, re.I)
        if m:
            model = m.group(1)

        # Start of timing report
        m = re.match(r".*:\s+Report Timing:\s+Found\s+(\d+)\s+(setup|hold)\s+paths\s\((\d+) violated\)\.\s+Worst case slack is\s+([-\.,0-9]+)", s, re.I)
        if not active and m:
            path_no    = int(m.group(1))
            path_type  = m.group(2)
            violated   = int(m.group(3))
            slack      = float(m.group(4))
            active     = path_no > 0 and violated > 0
        
        # Launch clock
        m = re.match(r".*:\s*Launch Clock\s*:\s*(\w+)", s, re.I)
        if active and m:
            launch_clk = m.group(1)

        # Latch Clock
        m = re.match(r".*:\s*Latch Clock\s*:\s*(\w+)", s, re.I)
        if active and m:
            latch_clk = m.group(1)
            fw.write("<h3>%s path from %s to %s</h3>\n" % (path_type.capitalize(), launch_clk, latch_clk))
            fw.write("<b>Timing Model:</b> %s<br>\n" % model)
            fw.write("<b>Slack:</b> %0.3f ns<br>\n" % slack)

        # Start of data arrival path
        m = re.match(r".*Data Arrival Path:", s, re.I)
        if active and m:
            fw.write("<h4>Data arrival path</h4>\n")
            fw.write("<table rules=all frame=border border=2>\n")
            fw.write("<thead>\n");
            fw.write("  <th align=right>Total [ns]</th><th align=right>Incr. [ns]</th><th>--</th><th>Type</th><th align=left>Element</th>\n");
            fw.write("</thead>\n");

        # Start of data required path
        m = re.match(r".*Data Required Path:", s, re.I)
        if active and m:
            fw.write("</table>\n")
            fw.write("<h4>Data required path</h4>\n")
            fw.write("<table rules=all frame=border border=2>\n")
            fw.write("<thead>\n");
            fw.write("  <th align=right>Total [ns]</th><th align=right>Incr. [ns]</th><th>--</th><th>Type</th><th align=left>Element</th>\n");
            fw.write("</thead>\n");

        # Timing entry
        m = re.match(r".*:\s*([-\.0-9]+)\s+([-\.0-9]+)\s+(..)\s+(uTco|CELL|IC|uTsu|    )\s+(.*)$", s, re.I)
        if active and m:
            total   = float(m.group(1))
            incr    = float(m.group(2))
            type1   = m.group(3)
            type2   = m.group(4)
            element = m.group(5).replace('\\','')
            fw.write("<tr>\n")
            fw.write("  <td align=right>%0.3f</td>\n" % total)
            fw.write("  <td align=right>%0.3f</td>\n" % incr)
            fw.write("  <td align=right>%s</td>\n" % type1)
            fw.write("  <td align=right>%s</td>\n" % type2)
            fw.write("  <td>%s</td>\n" % element)
            fw.write("</tr>\n")

        # End of timing report
        m = re.match(r".*: Slack\s+:.*", s, re.I)
        if active and m:
            active = False
            fw.write("</table>\n")
            fw.write("<br><hr>\n")


    ### for
    fw.write("</body>\n")
    fw.write("</html>\n")
    fw.close()
### def


if __name__ == "__main__":
    if len(sys.argv) < 2:
        error("Missing argument (Quartus revision name)")

    rev_name   = sys.argv[1]
    parse_sta_rpt(rev_name)


