added files
This commit is contained in:
parent
72ba761443
commit
940920d18b
6
.gitignore
vendored
6
.gitignore
vendored
@ -102,6 +102,12 @@
|
|||||||
*.html
|
*.html
|
||||||
*/*/bd/*/ip/*/*.tcl
|
*/*/bd/*/ip/*/*.tcl
|
||||||
*/*/*/bd/*/ip/*/*.tcl
|
*/*/*/bd/*/ip/*/*.tcl
|
||||||
|
db/
|
||||||
|
incremental_db/
|
||||||
|
output_files/
|
||||||
|
*.sof
|
||||||
|
*.pof
|
||||||
|
*.qws
|
||||||
hw_handoff
|
hw_handoff
|
||||||
ipshared
|
ipshared
|
||||||
|
|
||||||
|
|||||||
101
Logic2_D7.csv
Normal file
101
Logic2_D7.csv
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
Time [s],D7
|
||||||
|
0.0000000000000000,0
|
||||||
|
0.0000000010000000,0
|
||||||
|
0.0000000020000000,1
|
||||||
|
0.0000000030000000,1
|
||||||
|
0.0000000040000000,0
|
||||||
|
0.0000000050000000,0
|
||||||
|
0.0000000060000000,1
|
||||||
|
0.0000000070000000,1
|
||||||
|
0.0000000080000000,0
|
||||||
|
0.0000000090000000,0
|
||||||
|
0.0000000100000000,1
|
||||||
|
0.0000000110000000,1
|
||||||
|
0.0000000120000000,0
|
||||||
|
0.0000000130000000,0
|
||||||
|
0.0000000140000000,1
|
||||||
|
0.0000000150000000,1
|
||||||
|
0.0000000160000000,0
|
||||||
|
0.0000000170000000,0
|
||||||
|
0.0000000180000000,1
|
||||||
|
0.0000000190000000,1
|
||||||
|
0.0000000200000000,0
|
||||||
|
0.0000000210000000,0
|
||||||
|
0.0000000220000000,1
|
||||||
|
0.0000000230000000,1
|
||||||
|
0.0000000240000000,0
|
||||||
|
0.0000000250000000,0
|
||||||
|
0.0000000260000000,1
|
||||||
|
0.0000000270000000,1
|
||||||
|
0.0000000280000000,0
|
||||||
|
0.0000000290000000,0
|
||||||
|
0.0000000300000000,1
|
||||||
|
0.0000000310000000,1
|
||||||
|
0.0000000320000000,0
|
||||||
|
0.0000000330000000,0
|
||||||
|
0.0000000340000000,1
|
||||||
|
0.0000000350000000,1
|
||||||
|
0.0000000360000000,0
|
||||||
|
0.0000000370000000,0
|
||||||
|
0.0000000380000000,1
|
||||||
|
0.0000000390000000,1
|
||||||
|
0.0000000400000000,0
|
||||||
|
0.0000000410000000,0
|
||||||
|
0.0000000420000000,1
|
||||||
|
0.0000000430000000,1
|
||||||
|
0.0000000440000000,0
|
||||||
|
0.0000000450000000,0
|
||||||
|
0.0000000460000000,1
|
||||||
|
0.0000000470000000,1
|
||||||
|
0.0000000480000000,0
|
||||||
|
0.0000000490000000,0
|
||||||
|
0.0000000500000000,1
|
||||||
|
0.0000000510000000,1
|
||||||
|
0.0000000520000000,0
|
||||||
|
0.0000000530000000,0
|
||||||
|
0.0000000540000000,1
|
||||||
|
0.0000000550000000,1
|
||||||
|
0.0000000560000000,0
|
||||||
|
0.0000000570000000,0
|
||||||
|
0.0000000580000000,1
|
||||||
|
0.0000000590000000,1
|
||||||
|
0.0000000600000000,0
|
||||||
|
0.0000000610000000,0
|
||||||
|
0.0000000620000000,1
|
||||||
|
0.0000000630000000,1
|
||||||
|
0.0000000640000000,0
|
||||||
|
0.0000000650000000,0
|
||||||
|
0.0000000660000000,1
|
||||||
|
0.0000000670000000,1
|
||||||
|
0.0000000680000000,0
|
||||||
|
0.0000000690000000,0
|
||||||
|
0.0000000700000000,1
|
||||||
|
0.0000000710000000,1
|
||||||
|
0.0000000720000000,0
|
||||||
|
0.0000000730000000,0
|
||||||
|
0.0000000740000000,1
|
||||||
|
0.0000000750000000,1
|
||||||
|
0.0000000760000000,0
|
||||||
|
0.0000000770000000,0
|
||||||
|
0.0000000780000000,1
|
||||||
|
0.0000000790000000,1
|
||||||
|
0.0000000800000000,0
|
||||||
|
0.0000000810000000,0
|
||||||
|
0.0000000820000000,1
|
||||||
|
0.0000000830000000,1
|
||||||
|
0.0000000840000000,0
|
||||||
|
0.0000000850000000,0
|
||||||
|
0.0000000860000000,1
|
||||||
|
0.0000000870000000,1
|
||||||
|
0.0000000880000000,0
|
||||||
|
0.0000000890000000,0
|
||||||
|
0.0000000900000000,1
|
||||||
|
0.0000000910000000,1
|
||||||
|
0.0000000920000000,0
|
||||||
|
0.0000000930000000,0
|
||||||
|
0.0000000940000000,1
|
||||||
|
0.0000000950000000,1
|
||||||
|
0.0000000960000000,0
|
||||||
|
0.0000000970000000,0
|
||||||
|
0.0000000980000000,1
|
||||||
|
0.0000000990000000,1
|
||||||
|
30
Logic2_to_FPGA.qpf
Normal file
30
Logic2_to_FPGA.qpf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
#
|
||||||
|
# Copyright (C) 1991-2013 Altera Corporation
|
||||||
|
# Your use of Altera Corporation's design tools, logic functions
|
||||||
|
# and other software and tools, and its AMPP partner logic
|
||||||
|
# functions, and any output files from any of the foregoing
|
||||||
|
# (including device programming or simulation files), and any
|
||||||
|
# associated documentation or information are expressly subject
|
||||||
|
# to the terms and conditions of the Altera Program License
|
||||||
|
# Subscription Agreement, Altera MegaCore Function License
|
||||||
|
# Agreement, or other applicable license agreement, including,
|
||||||
|
# without limitation, that your use is for the sole purpose of
|
||||||
|
# programming logic devices manufactured by Altera and sold by
|
||||||
|
# Altera or its authorized distributors. Please refer to the
|
||||||
|
# applicable agreement for further details.
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
#
|
||||||
|
# Quartus II 64-Bit
|
||||||
|
# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition
|
||||||
|
# Date created = 05:32:19 June 01, 2026
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
QUARTUS_VERSION = "13.1"
|
||||||
|
DATE = "05:32:19 June 01, 2026"
|
||||||
|
|
||||||
|
# Revisions
|
||||||
|
|
||||||
|
PROJECT_REVISION = "Logic2_to_FPGA"
|
||||||
71
Logic2_to_FPGA.qsf
Normal file
71
Logic2_to_FPGA.qsf
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
#
|
||||||
|
# Copyright (C) 1991-2013 Altera Corporation
|
||||||
|
# Your use of Altera Corporation's design tools, logic functions
|
||||||
|
# and other software and tools, and its AMPP partner logic
|
||||||
|
# functions, and any output files from any of the foregoing
|
||||||
|
# (including device programming or simulation files), and any
|
||||||
|
# associated documentation or information are expressly subject
|
||||||
|
# to the terms and conditions of the Altera Program License
|
||||||
|
# Subscription Agreement, Altera MegaCore Function License
|
||||||
|
# Agreement, or other applicable license agreement, including,
|
||||||
|
# without limitation, that your use is for the sole purpose of
|
||||||
|
# programming logic devices manufactured by Altera and sold by
|
||||||
|
# Altera or its authorized distributors. Please refer to the
|
||||||
|
# applicable agreement for further details.
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
#
|
||||||
|
# Quartus II 64-Bit
|
||||||
|
# Version 13.1.0 Build 162 10/23/2013 SJ Web Edition
|
||||||
|
# Date created = 05:32:19 June 01, 2026
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
#
|
||||||
|
# 1) The default values for assignments are stored in the file:
|
||||||
|
# Logic2_to_FPGA_assignment_defaults.qdf
|
||||||
|
# If this file doesn't exist, see file:
|
||||||
|
# assignment_defaults.qdf
|
||||||
|
#
|
||||||
|
# 2) Altera recommends that you do not modify this file. This
|
||||||
|
# file is updated automatically by the Quartus II software
|
||||||
|
# and any changes you make may be lost or overwritten.
|
||||||
|
#
|
||||||
|
# -------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
set_global_assignment -name FAMILY "Cyclone IV E"
|
||||||
|
set_global_assignment -name DEVICE EP4CE6E22C8
|
||||||
|
set_global_assignment -name TOP_LEVEL_ENTITY top
|
||||||
|
set_global_assignment -name ORIGINAL_QUARTUS_VERSION 13.1
|
||||||
|
set_global_assignment -name PROJECT_CREATION_TIME_DATE "05:32:19 JUNE 01, 2026"
|
||||||
|
set_global_assignment -name LAST_QUARTUS_VERSION 13.1
|
||||||
|
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
|
||||||
|
set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0
|
||||||
|
set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85
|
||||||
|
set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1
|
||||||
|
set_global_assignment -name NOMINAL_CORE_SUPPLY_VOLTAGE 1.2V
|
||||||
|
set_global_assignment -name EDA_SIMULATION_TOOL "ModelSim-Altera (Verilog)"
|
||||||
|
set_global_assignment -name EDA_TIME_SCALE "1 ps" -section_id eda_simulation
|
||||||
|
set_global_assignment -name EDA_OUTPUT_DATA_FORMAT "VERILOG HDL" -section_id eda_simulation
|
||||||
|
set_global_assignment -name POWER_PRESET_COOLING_SOLUTION "23 MM HEAT SINK WITH 200 LFPM AIRFLOW"
|
||||||
|
set_global_assignment -name POWER_BOARD_THERMAL_MODEL "NONE (CONSERVATIVE)"
|
||||||
|
set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
|
||||||
|
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
|
||||||
|
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
|
||||||
|
set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "2.5 V"
|
||||||
|
set_location_assignment PIN_23 -to CLK50
|
||||||
|
set_location_assignment PIN_87 -to GPIO0
|
||||||
|
set_location_assignment PIN_7 -to KEY0
|
||||||
|
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to CLK50
|
||||||
|
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to GPIO0
|
||||||
|
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to KEY0
|
||||||
|
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
|
||||||
|
set_global_assignment -name SOURCE_FILE Logic2_to_FPGA.qpf
|
||||||
|
set_global_assignment -name SOURCE_FILE Logic2_to_FPGA.qsf
|
||||||
|
set_global_assignment -name SDC_FILE omdazz.sdc
|
||||||
|
set_global_assignment -name VERILOG_FILE top.v
|
||||||
|
set_global_assignment -name VERILOG_FILE pattern_rom.v
|
||||||
|
set_global_assignment -name VERILOG_FILE pattern_player.v
|
||||||
3
omdazz.sdc
Normal file
3
omdazz.sdc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
create_clock -period 20 -name clk [get_ports clk]
|
||||||
|
|
||||||
|
|
||||||
82
pattern_player.v
Normal file
82
pattern_player.v
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// pattern_player.v
|
||||||
|
// Проигрывает один раз последовательность сегментов из pattern_rom.
|
||||||
|
|
||||||
|
module pattern_player #(
|
||||||
|
parameter TICKS_WIDTH = 32,
|
||||||
|
parameter DEPTH = 128
|
||||||
|
)(
|
||||||
|
input wire clk,
|
||||||
|
input wire rst,
|
||||||
|
input wire start, // 1 → начать воспроизведение
|
||||||
|
output reg out, // выход на пин
|
||||||
|
output reg busy // 1, пока идёт воспроизведение
|
||||||
|
);
|
||||||
|
|
||||||
|
// Адрес в ROM
|
||||||
|
reg [$clog2(DEPTH)-1:0] addr;
|
||||||
|
|
||||||
|
// Текущие {level, ticks}
|
||||||
|
reg [TICKS_WIDTH:0] cur_data;
|
||||||
|
reg [TICKS_WIDTH-1:0] ticks_cnt;
|
||||||
|
|
||||||
|
wire [TICKS_WIDTH:0] rom_data;
|
||||||
|
|
||||||
|
// Экземпляр ROM (Автогенерированный pattern_rom.v от Python-скрипта)
|
||||||
|
pattern_rom #(
|
||||||
|
.TICKS_WIDTH(TICKS_WIDTH),
|
||||||
|
.DEPTH(DEPTH)
|
||||||
|
) rom_inst (
|
||||||
|
.addr(addr),
|
||||||
|
.data(rom_data)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Простая машина состояний
|
||||||
|
localparam IDLE = 2'd0;
|
||||||
|
localparam LOAD = 2'd1;
|
||||||
|
localparam RUN = 2'd2;
|
||||||
|
|
||||||
|
reg [1:0] state;
|
||||||
|
|
||||||
|
always @(posedge clk or posedge rst) begin
|
||||||
|
if (rst) begin
|
||||||
|
state <= IDLE;
|
||||||
|
addr <= 0;
|
||||||
|
ticks_cnt <= 0;
|
||||||
|
out <= 0;
|
||||||
|
busy <= 0;
|
||||||
|
end else begin
|
||||||
|
case (state)
|
||||||
|
IDLE: begin
|
||||||
|
busy <= 0;
|
||||||
|
if (start) begin
|
||||||
|
addr <= 0;
|
||||||
|
state <= LOAD;
|
||||||
|
busy <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
LOAD: begin
|
||||||
|
cur_data <= rom_data;
|
||||||
|
out <= rom_data[TICKS_WIDTH]; // уровень
|
||||||
|
ticks_cnt <= rom_data[TICKS_WIDTH-1:0]; // длительность
|
||||||
|
state <= RUN;
|
||||||
|
end
|
||||||
|
|
||||||
|
RUN: begin
|
||||||
|
if (ticks_cnt == 0) begin
|
||||||
|
if (addr == DEPTH - 1) begin
|
||||||
|
// Проиграли все сегменты один раз → назад в IDLE
|
||||||
|
state <= IDLE;
|
||||||
|
end else begin
|
||||||
|
addr <= addr + 1;
|
||||||
|
state <= LOAD;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
ticks_cnt <= ticks_cnt - 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
69
pattern_rom.v
Normal file
69
pattern_rom.v
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Автогенерация из CSV: Logic2_D7.csv
|
||||||
|
// F_CLK_HZ = 48000000
|
||||||
|
// Количество сегментов (DEPTH) = 50
|
||||||
|
|
||||||
|
module pattern_rom #(
|
||||||
|
parameter TICKS_WIDTH = 32,
|
||||||
|
parameter DEPTH = 50
|
||||||
|
) (
|
||||||
|
input wire [$clog2(DEPTH)-1:0] addr,
|
||||||
|
output reg [TICKS_WIDTH:0] data // {level[MSB], ticks[LSB:0]}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Простейший ROM на case по адресу
|
||||||
|
always @* begin
|
||||||
|
case (addr)
|
||||||
|
0: data = {1'b0, 32'd1};
|
||||||
|
1: data = {1'b1, 32'd1};
|
||||||
|
2: data = {1'b0, 32'd1};
|
||||||
|
3: data = {1'b1, 32'd1};
|
||||||
|
4: data = {1'b0, 32'd1};
|
||||||
|
5: data = {1'b1, 32'd1};
|
||||||
|
6: data = {1'b0, 32'd1};
|
||||||
|
7: data = {1'b1, 32'd1};
|
||||||
|
8: data = {1'b0, 32'd1};
|
||||||
|
9: data = {1'b1, 32'd1};
|
||||||
|
10: data = {1'b0, 32'd1};
|
||||||
|
11: data = {1'b1, 32'd1};
|
||||||
|
12: data = {1'b0, 32'd1};
|
||||||
|
13: data = {1'b1, 32'd1};
|
||||||
|
14: data = {1'b0, 32'd1};
|
||||||
|
15: data = {1'b1, 32'd1};
|
||||||
|
16: data = {1'b0, 32'd1};
|
||||||
|
17: data = {1'b1, 32'd1};
|
||||||
|
18: data = {1'b0, 32'd1};
|
||||||
|
19: data = {1'b1, 32'd1};
|
||||||
|
20: data = {1'b0, 32'd1};
|
||||||
|
21: data = {1'b1, 32'd1};
|
||||||
|
22: data = {1'b0, 32'd1};
|
||||||
|
23: data = {1'b1, 32'd1};
|
||||||
|
24: data = {1'b0, 32'd1};
|
||||||
|
25: data = {1'b1, 32'd1};
|
||||||
|
26: data = {1'b0, 32'd1};
|
||||||
|
27: data = {1'b1, 32'd1};
|
||||||
|
28: data = {1'b0, 32'd1};
|
||||||
|
29: data = {1'b1, 32'd1};
|
||||||
|
30: data = {1'b0, 32'd1};
|
||||||
|
31: data = {1'b1, 32'd1};
|
||||||
|
32: data = {1'b0, 32'd1};
|
||||||
|
33: data = {1'b1, 32'd1};
|
||||||
|
34: data = {1'b0, 32'd1};
|
||||||
|
35: data = {1'b1, 32'd1};
|
||||||
|
36: data = {1'b0, 32'd1};
|
||||||
|
37: data = {1'b1, 32'd1};
|
||||||
|
38: data = {1'b0, 32'd1};
|
||||||
|
39: data = {1'b1, 32'd1};
|
||||||
|
40: data = {1'b0, 32'd1};
|
||||||
|
41: data = {1'b1, 32'd1};
|
||||||
|
42: data = {1'b0, 32'd1};
|
||||||
|
43: data = {1'b1, 32'd1};
|
||||||
|
44: data = {1'b0, 32'd1};
|
||||||
|
45: data = {1'b1, 32'd1};
|
||||||
|
46: data = {1'b0, 32'd1};
|
||||||
|
47: data = {1'b1, 32'd1};
|
||||||
|
48: data = {1'b0, 32'd1};
|
||||||
|
49: data = {1'b1, 32'd1};
|
||||||
|
default: data = {1'b0, {TICKS_WIDTH{1'b0}}};
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
244
to_FPGA.py
Normal file
244
to_FPGA.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
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()
|
||||||
53
top.v
Normal file
53
top.v
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// top.v — верхний уровень для Cyclone IV (OMDAZZ)
|
||||||
|
// Внутри — pattern_player + pattern_rom (ROM сгенерирован Python-скриптом)
|
||||||
|
|
||||||
|
module top (
|
||||||
|
input wire CLK50, // системный клок с платы (например, 50 МГц)
|
||||||
|
input wire KEY0, // кнопка reset (на OMDAZZ часто активный низ)
|
||||||
|
output wire GPIO0 // выход на пин (оптопара/LED и т.п.)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Сброс (reset)
|
||||||
|
// ==========================
|
||||||
|
// Предположим, что KEY0 замыкает на 0, когда нажата → делаем rst = !KEY0.
|
||||||
|
wire rst = ~KEY0;
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Сигнал запуска плеера
|
||||||
|
// ==========================
|
||||||
|
// Для начала можем просто держать start = 1,
|
||||||
|
// чтобы паттерн проигрывался один раз после сброса.
|
||||||
|
// Если нужен запуск по кнопке — можно сделать простую логіку отдельно.
|
||||||
|
wire start = 1'b1;
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Параметры должны совпадать с pattern_rom.v
|
||||||
|
// ==========================
|
||||||
|
localparam TICKS_WIDTH = 32;
|
||||||
|
// DEPTH возьми из комментария в созданном pattern_rom.v
|
||||||
|
// (Python-скрипт его печатает). Например:
|
||||||
|
localparam DEPTH = 128;
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Соединения с pattern_player
|
||||||
|
// ==========================
|
||||||
|
wire out_sig;
|
||||||
|
wire busy;
|
||||||
|
|
||||||
|
pattern_player #(
|
||||||
|
.TICKS_WIDTH(TICKS_WIDTH),
|
||||||
|
.DEPTH(DEPTH)
|
||||||
|
) u_player (
|
||||||
|
.clk (CLK50), // тот же клок, что и в Python (F_CLK_HZ = 50e6)
|
||||||
|
// если F_CLK_HZ другое — нужен PLL и другой сигнал clk
|
||||||
|
.rst (rst),
|
||||||
|
.start (start),
|
||||||
|
.out (out_sig),
|
||||||
|
.busy (busy)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Выводим сигнал на внешний пин
|
||||||
|
assign GPIO0 = out_sig;
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue
Block a user