Logic2_to_FPGA/to_FPGA.py
2026-06-01 16:41:22 +03:00

244 lines
8.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import csv
from pathlib import Path
# ==========================
# НАСТРОЙКИ ПОЛЬЗОВАТЕЛЯ
# ==========================
# Входной файл из Logic 2 (CSV только с Time [s] и D7)
CSV_FILE = "digital_cut_50.csv"
# Выходной Verilog-файл
VERILOG_FILE = "pattern_rom.v"
# Тактовая частота в МК/FPGA, Гц (должна совпадать с тем, что реально в железе)
F_CLK_HZ = 48_000_000 # пример: 48 МГц
# Имена колонок в CSV (посмотри заголовок в своём файле и подгони при необходимости)
TIME_COL = "Time [s]" # колонка с временем
LEVEL_COL = "Channel 7" # колонка с уровнем сигнала
# Сколько бит отвести под счётчик тиков в 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()