import csv
from pathlib import Path

# ==========================
# НАСТРОЙКИ ПОЛЬЗОВАТЕЛЯ
# ==========================

# Входной файл из Logic 2 (CSV только с Time [s] и D7)
CSV_FILE = "Logic2_D7.csv"

# Выходной Verilog-файл
VERILOG_FILE = "pattern_rom.v"

# Тактовая частота в МК/FPGA, Гц (должна совпадать с тем, что реально в железе)
F_CLK_HZ = 48_000_000  # пример: 48 МГц

# Имена колонок в CSV (посмотри заголовок в своём файле и подгони при необходимости)
TIME_COL = "Time [s]"  # колонка с временем
LEVEL_COL = "D7"       # колонка с уровнем сигнала

# Сколько бит отвести под счётчик тиков в Verilog
TICKS_WIDTH = 32


# ==========================
# ЧТЕНИЕ CSV ИЗ LOGIC 2
# ==========================

def read_digital_trace(csv_path: str):
    """
    Читает CSV-файл в формате Logic 2 и возвращает два списка:
      times[i]  - время в секундах (float),
      levels[i] - уровень (0 или 1) в моменты времени times[i].

    Ожидается, что первая строка CSV — заголовок с колонками TIME_COL и LEVEL_COL.
    """
    times = []
    levels = []

    with open(csv_path, newline="") as f:
        reader = csv.DictReader(f)

        # Проверяем наличие нужных столбцов
        if TIME_COL not in reader.fieldnames or LEVEL_COL not in reader.fieldnames:
            raise RuntimeError(
                f"В CSV нет ожидаемых колонок '{TIME_COL}' и/или '{LEVEL_COL}'. "
                f"Найденные столбцы: {reader.fieldnames}"
            )

        # Проходим по всем строкам после заголовка
        for row in reader:
            # Время
            t = float(row[TIME_COL])
            # Уровень (0 или 1)
            lvl = int(row[LEVEL_COL])

            times.append(t)
            levels.append(lvl)

    if not times:
        raise RuntimeError("CSV пустой или не содержит данных (только заголовок).")

    return times, levels


# ==========================
# ПОСТРОЕНИЕ СЕГМЕНТОВ
# ==========================

def build_segments(times, levels):
    """
    Строит сегменты постоянного уровня.

    На входе:
      times[i], levels[i] — выборки (временная метка + уровень).

    На выходе:
      segments: список кортежей (level, duration_sec), где:
        level        — 0 или 1,
        duration_sec — длительность в секундах (> 0).

    Пример:
      times  = [0.0, 1e-6, 2e-6, 3e-6]
      levels = [0,   0,    1,    1   ]

      → segments = [(0, 2e-6), (1, 1e-6)]
    """
    segments = []

    current_level = levels[0]
    start_time = times[0]

    # Идём по всем точкам, начиная со второй
    for i in range(1, len(times)):
        t = times[i]
        lvl = levels[i]

        # Если уровень поменялся — значит, закончился сегмент
        if lvl != current_level:
            duration = t - start_time
            if duration > 0:
                segments.append((current_level, duration))
            # Начинаем новый сегмент с новым уровнем
            start_time = t
            current_level = lvl

    # Последний сегмент: от последней смены до конца записи
    end_time = times[-1]
    duration = end_time - start_time
    if duration > 0:
        segments.append((current_level, duration))

    # Если ни одного сегмента не получилось (например, уровень вообще не менялся)
    if not segments:
        full_duration = times[-1] - times[0]
        if full_duration <= 0:
            raise RuntimeError("Не удалось построить сегменты: время не растёт.")
        segments.append((levels[0], full_duration))

    return segments


# ==========================
# КВАНТОВАНИЕ В ТИКИ
# ==========================

def quantize_segments(segments, f_clk_hz: float):
    """
    Переводит длительность сегментов из секунд в количество тиков таймера.

    На входе:
      segments: список (level, duration_sec)

    На выходе:
      segments_ticks: список (level, ticks)
        ticks — целое положительное число (>= 1)
    """
    result = []

    for idx, (level, duration_sec) in enumerate(segments):
        # Перевод секунд в тики
        ticks = round(duration_sec * f_clk_hz)

        # Страховка от нулевой длительности
        if ticks <= 0:
            ticks = 1

        result.append((level, ticks))

    return result


# ==========================
# ГЕНЕРАЦИЯ VERILOG ROM
# ==========================

def generate_verilog_rom(segments_ticks, out_path: str):
    """
    Генерирует Verilog-модуль pattern_rom с ROM вида:
      data = {level, ticks}, где
        level — самый старший бит (1 бит),
        ticks — младшие TICKS_WIDTH бит.

    Интерфейс модуля:
      module pattern_rom #(
          parameter TICKS_WIDTH = ...,
          parameter DEPTH = ...
      )(
          input  wire [$clog2(DEPTH)-1:0] addr,
          output reg  [TICKS_WIDTH:0]     data
      );

      // data[TICKS_WIDTH]     - уровень (0 или 1)
      // data[TICKS_WIDTH-1:0] - ticks
    """
    depth = len(segments_ticks)

    lines = []
    lines.append(f"// Автогенерация из CSV: {CSV_FILE}")
    lines.append(f"// F_CLK_HZ = {F_CLK_HZ}")
    lines.append(f"// Количество сегментов (DEPTH) = {depth}")
    lines.append("")
    lines.append("module pattern_rom #(")
    lines.append(f"    parameter TICKS_WIDTH = {TICKS_WIDTH},")
    lines.append(f"    parameter DEPTH = {depth}")
    lines.append(") (")
    lines.append("    input  wire [$clog2(DEPTH)-1:0] addr,")
    lines.append("    output reg  [TICKS_WIDTH:0] data  // {level[MSB], ticks[LSB:0]}")
    lines.append(");")
    lines.append("")
    lines.append("    // Простейший ROM на case по адресу")
    lines.append("    always @* begin")
    lines.append("        case (addr)")

    for i, (level, ticks) in enumerate(segments_ticks):
        # Проверяем, помещается ли длительность в TICKS_WIDTH бит
        if ticks >= (1 << TICKS_WIDTH):
            raise ValueError(
                f"Сегмент {i}: ticks={ticks} не помещается в {TICKS_WIDTH} бит. "
                "Увеличьте TICKS_WIDTH или уменьшите F_CLK_HZ."
            )

        # Строка ROM:
        #   i: data = {1'bL, TICKS_WIDTH'dticks};
        lines.append(
            f"            {i}: data = {{1'b{level}, {TICKS_WIDTH}'d{ticks}}};"
        )

    # Значение по умолчанию (на всякий случай)
    lines.append("            default: data = {1'b0, {TICKS_WIDTH{1'b0}}};")
    lines.append("        endcase")
    lines.append("    end")
    lines.append("endmodule")
    lines.append("")

    # Запись Verilog-файла
    Path(out_path).write_text("\n".join(lines), encoding="utf-8")


# ==========================
# ТОЧКА ВХОДА
# ==========================

def main():
    # 1. Читаем CSV Logic 2
    times, levels = read_digital_trace(CSV_FILE)

    # 2. Строим сегменты постоянного уровня
    segments = build_segments(times, levels)

    # 3. Переводим длительность сегментов в тики тактового генератора
    segments_ticks = quantize_segments(segments, F_CLK_HZ)

    # 4. Генерируем Verilog ROM
    generate_verilog_rom(segments_ticks, VERILOG_FILE)

    # Немного статистики в консоль
    print(f"OK: прочитано выборок: {len(times)}")
    print(f"    построено сегментов: {len(segments_ticks)}")
    print(f"    Verilog ROM записан: {VERILOG_FILE}")


if __name__ == "__main__":
    main()