upd: reflectometer on axis bus iface + TB

This commit is contained in:
2026-07-10 18:46:48 +03:00
parent 0ba25a3541
commit 11a9e97691
5 changed files with 199 additions and 124 deletions

View File

@ -10,16 +10,21 @@
FPGA_PART = xc7a100tfgg484-2
FPGA_TOP = reflectometer_top
FPGA_ARCH = artix7
SIM_TOP = reflectometer_tb
RTL_DIR = ../../rtl
include ../../scripts/vivado.mk
INC_FILES += interfaces.svh
TB_FILES += reflectometer_tb.sv
SYN_FILES += reflectometer.sv
SYN_FILES += dac_model.sv
SYN_FILES += adc_model.sv
SYN_FILES += tb_reflectometer.sv
SYN_FILES += reflectometer_tb.sv
SYN_FILES += $(sort $(shell find ../../rtl -type f \( -name '*.v' -o -name '*.sv' \)))
XCI_FILES = $(sort $(shell find ../../rtl/ethernet-udp/src -type f -name '*.xci'))
@ -28,7 +33,6 @@ XCI_FILES += $(sort $(shell find ip/ -type f -name '*.xci'))
XDC_FILES += ../../constraints/ax7102.xdc
XDC_FILES += debug.xdc
SIM_TOP = tb_reflectometer
program: $(PROJECT).bit

View File

@ -2,3 +2,8 @@
create_clock -name ref_clock -period 5.000 [get_ports clk_in]
create_clock -name phy_rx_clock -period 8.000 [get_ports clk_m_axis]
create_clock -name phy_tx_clock -period 8.000 [get_ports clk_s_axis]
set clk_125_name [get_clocks -of_objects [get_pins generator_inst/clk_dac_125]]
set clk_65_name [get_clocks -of_objects [get_pins accumulator_top_dut/clk_adc_65]]
set_clock_groups -asynchronous -group $clk_125_name -group $clk_65_name

View File

@ -0,0 +1,53 @@
`ifndef AXIS_INTERFACE_SVH
`define AXIS_INTERFACE_SVH
interface axis_if #(
parameter int DATA_WIDTH = 8
)(
input logic clk,
input logic rst_n
);
// Сигналы шины AXI-Stream
logic [DATA_WIDTH-1:0] tdata;
logic tvalid;
logic tlast;
logic tready;
//------------------------------------------------------------
// Clocking block для Testbench (Driver/Monitor)
// Защищает от Race Conditions в симуляции
//------------------------------------------------------------
clocking drv_cb @(posedge clk);
default input #1step output #100ps;
output tdata, tvalid, tlast;
input tready;
endclocking
clocking mon_cb @(posedge clk);
default input #1step;
input tdata, tvalid, tlast, tready;
endclocking
//------------------------------------------------------------
// Modports для автоматического подключения одной строчкой
//------------------------------------------------------------
modport master (
output tdata, tvalid, tlast,
input tready
);
modport slave (
input tdata, tvalid, tlast,
output tready
);
// Modport для тестбенча (чтобы работать через функции)
modport tb (
clocking drv_cb,
clocking mon_cb
);
endinterface
`endif // AXIS_INTERFACE_SVH

View File

@ -1,5 +1,7 @@
`timescale 1 ns / 1 ns
`include "interfaces.svh"
module reflectometer_top #(
parameter int unsigned DAC_DATA_WIDTH = 14,
parameter int unsigned ADC_DATA_WIDTH = 12,
@ -16,18 +18,12 @@ module reflectometer_top #(
output wire locked,
// Accumulator AXI-S bus
input wire clk_m_axis, // GMII PHY RX clock
output wire [7:0] m_axis_tdata,
output wire m_axis_tvalid,
input wire m_axis_tready,
output wire m_axis_tlast,
input wire clk_axis_accumulator, // GMII PHY RX clock
axis_if.master axis_accumulator,
// Control AXI-S bus
input wire clk_s_axis, // GMII PHY TX clock
input wire [7:0] s_axis__tdata,
input wire s_axis_tvalid,
input wire s_axis_tlast,
output wire s_axis_tready,
input wire clk_axis_control, // GMII PHY TX clock
axis_if.slave axis_control,
// RTL-MAC handshake
input wire request_ready,
@ -101,15 +97,15 @@ module reflectometer_top #(
control #(
.DAC_DATA_WIDTH(DAC_DATA_WIDTH)
) udp_ctrl_inst (
.eth_clk_in (clk_s_axis),
.eth_clk_in (clk_axis_control),
.dac_clk_in (clk_generator),
.adc_clk_in (clk_sampler),
.rst_n (ctrl_rst_n),
.s_axis_tdata (s_axis__tdata),
.s_axis_tvalid (s_axis_tvalid),
.s_axis_tready (s_axis_tready),
.s_axis_tlast (s_axis_tlast),
.s_axis_tdata (axis_control.tdata),
.s_axis_tvalid (axis_control.tvalid),
.s_axis_tready (axis_control.tready),
.s_axis_tlast (axis_control.tlast),
.finish (finish),
@ -131,9 +127,9 @@ module reflectometer_top #(
//------------------------------------------------------------
// DAC -> ADC CDC
//------------------------------------------------------------
logic [2:0] stretch; // 125/65~=2. Чтобы поймать единичный импульс, растянем его во времени
logic [1:0] sync_DA;
wire dac_done_stretched;
(* ASYNC_REG = "TRUE" *) logic [2:0] stretch; // 125/65~=2. Чтобы поймать единичный импульс, растянем его во времени
(* ASYNC_REG = "TRUE" *) logic [1:0] sync_DA;
wire dac_done_stretched;
wire generator_done, generator_request;
wire sampler_done, sampler_request;
@ -163,7 +159,7 @@ module reflectometer_top #(
//------------------------------------------------------------
// ADC -> DAC CDC
//------------------------------------------------------------
logic [1:0] sync_AD;
(* ASYNC_REG = "TRUE" *) logic [1:0] sync_AD;
always_ff @(posedge clk_generator or posedge dac_rst) begin
if (dac_rst)
@ -237,11 +233,11 @@ module reflectometer_top #(
.req_ready(request_ready),
.send_req(send_request),
.eth_clk_in(clk_m_axis),
.m_axis_tdata(m_axis_tdata),
.m_axis_tvalid(m_axis_tvalid),
.m_axis_tready(m_axis_tready),
.m_axis_tlast(m_axis_tlast),
.eth_clk_in(clk_axis_accumulator),
.m_axis_tdata(axis_accumulator.tdata),
.m_axis_tvalid(axis_accumulator.tvalid),
.m_axis_tready(axis_accumulator.tready),
.m_axis_tlast(axis_accumulator.tlast),
.finish(finish)
);

View File

@ -1,6 +1,8 @@
`timescale 1ns / 1ps
module tb_reflectometer;
`include "interfaces.svh"
module reflectometer_tb;
//------------------------------------------------------------
// Параметры
//------------------------------------------------------------
@ -42,15 +44,15 @@ module tb_reflectometer;
// Внутренние сигналы тестбенча
//------------------------------------------------------------
// AXI-S интерфейс для управления
logic [7:0] m_axis_control_tdata = 8'b0;
logic m_axis_control_tvalid = 1'b0;
wire m_axis_control_tready;
logic m_axis_control_tlast = 1'b0;
axis_if axis_control_if (
.clk(clk_eth_phy),
.rst_n(rst_n)
);
// AXI-S интерфейс для данных
wire [7:0] s_axis_accumulator_tdata;
wire s_axis_accumulator_tvalid;
logic s_axis_accumulator_tready = 1'b0;
wire s_axis_accumulator_tlast;
axis_if axis_accumulator_if (
.clk(clk_eth_phy),
.rst_n(rst_n)
);
// DAC интерфейс
wire clk_dac;
wire dac_wrt;
@ -114,18 +116,12 @@ module tb_reflectometer;
.locked(mmcm_locked),
// Accumulator AXI-S bus
.clk_m_axis(clk_eth_phy), // GMII PHY RX clock
.m_axis_tdata(s_axis_accumulator_tdata),
.m_axis_tvalid(s_axis_accumulator_tvalid),
.m_axis_tready(s_axis_accumulator_tready),
.m_axis_tlast(s_axis_accumulator_tlast),
.clk_axis_accumulator(clk_eth_phy), // GMII PHY RX clock
.axis_accumulator(axis_accumulator_if.master),
// Control AXI-S bus
.clk_s_axis(clk_eth_phy), // GMII PHY TX clock
.s_axis__tdata(maxis_control_tdata),
.s_axis_tvalid(maxis_control_tvalid),
.s_axis_tlast(maxis_control_tlast),
.s_axis_tready(maxis_control_tready),
.clk_axis_control(clk_eth_phy), // GMII PHY TX clock
.axis_control(axis_control_if.slave),
// RTL-MAC handshake
.request_ready(request_ready),
@ -154,86 +150,100 @@ module tb_reflectometer;
// Таски для тестирования
//------------------------------------------------------------
// task automatic axis_send_byte(
// ref logic clk,
// input logic [7:0] data,
// input logic last,
// ref logic tvalid,
// ref logic [7:0] tdata,
// ref logic tlast,
// input logic tready
// );
// @(posedge clk);
// tdata <= data;
// tlast <= last;
// tvalid <= 1'b1;
// AXI-S таски
task automatic axis_send_packet(
virtual axis_if.tb vif,
const ref logic [7:0] payload[]
);
int packet_size = payload.size();
// // Ждем готовности приемника
// wait(tready === 1'b1);
if (packet_size == 0) begin
$display("[WARNING] -axis_send_packet- Попытка отправить пустой пакет.");
return;
end
// @(posedge clk);
// tvalid <= 1'b0;
// tlast <= 1'b0;
// endtask
foreach (payload[i]) begin
vif.drv_cb.tdata <= payload[i];
vif.drv_cb.tvalid <= 1'b1;
vif.drv_cb.tlast <= (i == packet_size - 1); // tlast активен только на последнем байте
// task automatic dut_soft_reset();
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(8'b00001111),
// .last(1'b1),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// endtask
// Шагаем на следующий фронт клока, чтобы симулятор применил выходы
@(vif.drv_cb);
// task automatic dut_start();
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(8'b11110000),
// .last(1'b1),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// endtask
// Ждем на последующих фронтах, пока tready не станет равен 1.
while (vif.drv_cb.tready !== 1'b1) begin
@(vif.drv_cb);
end
end
// task automatic dut_send_config(
// input logic [127:0] ctrl_config
// );
// // команда set_data
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(8'b10001000),
// .last(1'b0),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// // config burst
// for (int i = 0; i < 16; i++) begin
// logic [7:0] byte_to_send;
// logic is_last;
// Снимаем валидность на следующем дельта-цикле
vif.drv_cb.tvalid <= 1'b0;
vif.drv_cb.tlast <= 1'b0;
vif.drv_cb.tdata <= '0;
endtask
// // get byte
// byte_to_send = ctrl_config[i*8 +: 8];
// // tlast for last byte
// is_last = (i == 15);
task automatic dut_soft_reset();
logic [7:0] cmd[] = '{8'b00001111};
axis_send_packet(
.vif(axis_control_if.tb),
.payload(cmd)
);
endtask
// axis_send_byte(
// .clk(clk_eth_phy_rx),
// .data(byte_to_send),
// .last(is_last),
// .tvalid(m_axis_rx_tvalid),
// .tdata(m_axis_rx_tdata),
// .tlast(m_axis_rx_tlast),
// .tready(m_axis_rx_tready)
// );
// end
// endtask
task automatic dut_start();
logic [7:0] cmd[] = '{8'b11110000};
axis_send_packet(
.vif(axis_control_if.tb),
.payload(cmd)
);
endtask
task automatic dut_send_system_config(
input logic [31:0] pulse_width,
input logic [31:0] pulse_period,
input logic [15:0] pulse_num,
input logic [13:0] pulse_height,
input logic [31:0] pulse_period_adc
);
// Выделяем 17 байт: 1 байт команды + 16 байт регистров
logic [7:0] full_packet[] = new[17];
// 1. Сначала пишем команду в нулевой байт пакета
full_packet[0] = 8'b10001000; // Твоя команда set_data
// 2. Упаковываем регистр pulse_width [31:0] (байты 1..4)
full_packet[1] = pulse_width[7:0];
full_packet[2] = pulse_width[15:8];
full_packet[3] = pulse_width[23:16];
full_packet[4] = pulse_width[31:24];
// 3. Упаковываем регистр pulse_period [63:32] (байты 5..8)
full_packet[5] = pulse_period[7:0];
full_packet[6] = pulse_period[15:8];
full_packet[7] = pulse_period[23:16];
full_packet[8] = pulse_period[31:24];
// 4. Упаковываем регистр pulse_num [79:64] (байты 9..10)
full_packet[9] = pulse_num[7:0];
full_packet[10] = pulse_num[15:8];
// 5. Упаковываем регистр pulse_height [79+dac_width:80] (байты 11..12)
full_packet[11] = pulse_height[7:0];
full_packet[12] = {2'b00, pulse_height[13:8]}; // дополняем до честного байта
// 6. Упаковываем регистр pulse_period_adc [127:96] (байты 13..16)
full_packet[13] = pulse_period_adc[7:0];
full_packet[14] = pulse_period_adc[15:8];
full_packet[15] = pulse_period_adc[23:16];
full_packet[16] = pulse_period_adc[31:24];
axis_send_packet(
.vif(axis_control_if.tb),
.payload(full_packet)
);
endtask
// Таски сбора и обработки статистики
//------------------------------------------------------------
@ -245,10 +255,17 @@ module tb_reflectometer;
// Инициализация
rst_n = 1;
wait(mmcm_locked === 1'b1);
#10;
#100;
dut_soft_reset();
dut_send_system_config(
.pulse_width(32'd350),
.pulse_period(32'd1250),
.pulse_num(16'd10),
.pulse_height(14'd14000), // 0V
.pulse_period_adc(32'd650)
);
dut_start();
$display("[TB] ALL PASSED");
$finish;