#!/usr/bin/env python3
"""
Converts Renode log to execution trace for RISC-V DV
"""

import argparse
import os
import re
import sys
import logging

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))

from riscv_trace_csv import *
from lib import *

# =============================================================================

GPR_NAMES = [
    ("x0",  "zero"),
    ("x1",  "ra"),
    ("x2",  "sp"),
    ("x3",  "gp"),
    ("x4",  "tp"),
    ("x5",  "t0"),
    ("x6",  "t1"),
    ("x7",  "t2"),
    ("x8",  "s0"),
    ("x9",  "s1"),
    ("x10", "a0"),
    ("x11", "a1"),
    ("x12", "a2"),
    ("x13", "a3"),
    ("x14", "a4"),
    ("x15", "a5"),
    ("x16", "a6"),
    ("x17", "a7"),
    ("x18", "s2"),
    ("x19", "s3"),
    ("x20", "s4"),
    ("x21", "s5"),
    ("x22", "s6"),
    ("x23", "s7"),
    ("x24", "s8"),
    ("x25", "s9"),
    ("x26", "s10"),
    ("x27", "s11"),
    ("x28", "t3"),
    ("x29", "t4"),
    ("x30", "t5"),
    ("x31", "t6"),
]

# =============================================================================


def process_renode_sim_log(log_name, csv_name):
    """
    Converts a Renode trace log to CSV format
    """

    # Build lookups
    gpr_to_name = {m[0]: m[1] for m in GPR_NAMES}
    known_gpr = {m[0].upper() for m in GPR_NAMES}

    state = None
    trace = []
    with open(log_name, "r") as fp:
        for line in fp:

            line = line.strip()
            if not line:
                continue

            # We've hit ecall, quit
            if line.startswith("ECALL:"):
                break

            # Skip non-regdump
            if not line.startswith("REGDUMP:"):
                continue

            # Decode state
            fields = line.replace("REGDUMP:", "").split(",")
            regs = {fields[i]: fields[i+1] for i in range(0, len(fields), 2)}

            if state:
                # Compute state difference
                diff = {r: regs[r] for r in known_gpr \
                         if r in state and r in regs and state[r] != regs[r]}

                # Format the entry
                entry = RiscvInstructionTraceEntry()
                entry.pc        = f"{int(state['PC'], 16):08x}"
                entry.binary    = "0"
                entry.operand   = ""
                entry.mode      = "0"

                # GPRs
                for x in range(32):
                    name = "X{}".format(x)
                    if name in diff:
                        lname = name.lower()
                        value = int(diff[name], 16)
                        entry.gpr.append("{}:{:08x}".format(gpr_to_name[lname], value))

                # CSRs
                # TODO:

                # Add only if there is a GPR/CSR change
                if entry.gpr or entry.csr:
                    trace.append(entry)

            state = regs

    return trace


def write_csv(file_name, data):
    """
    Writes the trace to CSV
    """

    with open(file_name, "w") as fp:

        writer = RiscvInstructionTraceCsv(fp)
        writer.start_new_trace()

        for entry in data:
            writer.write_trace_entry(entry)

# ============================================================================


def main():
    # Parse input arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("--log", type=str, help="Input Renode simulation log")
    parser.add_argument("--csv", type=str, help="Output trace CSV file")
    parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
                        help="Verbose logging")
    parser.set_defaults(verbose=False)

    args = parser.parse_args()
    setup_logging(args.verbose)

    # Process Renode log
    trace = process_renode_sim_log(args.log, args.csv)
    # Write CSV
    write_csv(args.csv, trace)


if __name__ == "__main__":
    main()
