286 lines
10 KiB
Systemverilog
286 lines
10 KiB
Systemverilog
`timescale 1ns / 1ps
|
||
|
||
`include "interfaces.svh"
|
||
|
||
module reflectometer_tb;
|
||
//------------------------------------------------------------
|
||
// Параметры
|
||
//------------------------------------------------------------
|
||
localparam int unsigned DAC_DATA_WIDTH = 14;
|
||
localparam int unsigned ADC_DATA_WIDTH = 12;
|
||
localparam LOGIC_ZERO_LEVEL = 0; // DAC -5V for logic zero
|
||
localparam VOLTAGE_ZERO_LEVEL = 2**(DAC_DATA_WIDTH-1); // DAC 0V for logic zero
|
||
localparam PACK_FACTOR = 1; // not used in TB
|
||
localparam PROCESS_MODE = 1; // 0 - uint, 1 - int
|
||
localparam ACCUM_WIDTH = 32; // accumulator number bit witdth
|
||
localparam N_MAX = 4096; // max value of windows to average by experiments
|
||
localparam WINDOW_SIZE = 65; // fixed subwindow size to average by time
|
||
localparam PACKET_SIZE = 1024; // bytes per UDP packet
|
||
|
||
localparam ZERO_LEVEL = LOGIC_ZERO_LEVEL; // "logic" VS "voltage"
|
||
|
||
localparam CLK_ETH_PHY_PERIOD = 8.000; // 125 MHz
|
||
localparam CLK_REF_PERIOD = 5.000; // 200 MHz
|
||
|
||
//------------------------------------------------------------
|
||
// Тактовые сигналы и сброс
|
||
//------------------------------------------------------------
|
||
logic clk_ref = 1'b0; // 200 MHz
|
||
logic clk_eth_phy = 1'b0; // common for RX & TX
|
||
logic rst_n = 1'b0;
|
||
//------------------------------------------------------------
|
||
// Управление и конфиг
|
||
//------------------------------------------------------------
|
||
|
||
//------------------------------------------------------------
|
||
// Входы
|
||
//------------------------------------------------------------
|
||
|
||
//------------------------------------------------------------
|
||
// Выходы
|
||
//------------------------------------------------------------
|
||
wire mmcm_locked;
|
||
//------------------------------------------------------------
|
||
// Внутренние сигналы тестбенча
|
||
//------------------------------------------------------------
|
||
// AXI-S интерфейс для управления
|
||
axis_if axis_control_if (
|
||
.clk(clk_eth_phy),
|
||
.rst_n(rst_n)
|
||
);
|
||
// AXI-S интерфейс для данных
|
||
axis_if axis_accumulator_if (
|
||
.clk(clk_eth_phy),
|
||
.rst_n(rst_n)
|
||
);
|
||
// DAC интерфейс
|
||
wire clk_dac;
|
||
wire dac_wrt;
|
||
wire [DAC_DATA_WIDTH-1:0] dac_data;
|
||
// ADC интерфейс
|
||
wire clk_adc;
|
||
wire adc_otr;
|
||
wire [ADC_DATA_WIDTH-1:0] adc_data;
|
||
// Интерфейс хендшейка с MAC-PHY
|
||
wire send_request;
|
||
logic request_ready;
|
||
// Сигналы ЦАП и АЦП
|
||
real signal_voltage;
|
||
|
||
//------------------------------------------------------------
|
||
// Virtual DAC
|
||
//------------------------------------------------------------
|
||
virtual_dac_model #( // default voltage range is +/- 5V
|
||
.DAC_DATA_WIDTH(DAC_DATA_WIDTH)
|
||
// ,.VOLTAGE_GAIN(2)
|
||
) virtual_dac (
|
||
.clk_i(clk_dac),
|
||
.wrt_i(dac_wrt),
|
||
.data_i(dac_data),
|
||
.voltage_o(signal_voltage)
|
||
);
|
||
//------------------------------------------------------------
|
||
// Virtual ADC
|
||
//------------------------------------------------------------
|
||
virtual_adc_model #( // default voltage range is +/- 5V
|
||
.ADC_DATA_WIDTH(ADC_DATA_WIDTH)
|
||
) virtual_adc (
|
||
.clk_i(clk_adc),
|
||
.voltage_i(signal_voltage),
|
||
.otr_o(adc_otr),
|
||
.data_o(adc_data)
|
||
);
|
||
//------------------------------------------------------------
|
||
// Statistics monitor
|
||
//------------------------------------------------------------
|
||
|
||
//------------------------------------------------------------
|
||
// Config handler
|
||
//------------------------------------------------------------
|
||
|
||
//------------------------------------------------------------
|
||
// DUT
|
||
//------------------------------------------------------------
|
||
reflectometer_top #(
|
||
.DAC_DATA_WIDTH(DAC_DATA_WIDTH),
|
||
.ADC_DATA_WIDTH(ADC_DATA_WIDTH),
|
||
.PACK_FACTOR(PACK_FACTOR),
|
||
.PROCESS_MODE(PROCESS_MODE),
|
||
.ZERO_LEVEL(ZERO_LEVEL),
|
||
.ACCUM_WIDTH(ACCUM_WIDTH),
|
||
.N_MAX(N_MAX),
|
||
.WINDOW_SIZE(WINDOW_SIZE),
|
||
.PACKET_SIZE(PACKET_SIZE)
|
||
) DUT (
|
||
.clk_in(clk_ref),
|
||
.rst_n(rst_n),
|
||
.locked(mmcm_locked),
|
||
|
||
// Accumulator AXI-S bus
|
||
.clk_axis_accumulator(clk_eth_phy), // GMII PHY RX clock
|
||
.axis_accumulator(axis_accumulator_if.master),
|
||
|
||
// Control AXI-S bus
|
||
.clk_axis_control(clk_eth_phy), // GMII PHY TX clock
|
||
.axis_control(axis_control_if.slave),
|
||
|
||
// RTL-MAC handshake
|
||
.request_ready(request_ready),
|
||
.send_request(send_request),
|
||
|
||
// DAC
|
||
.dac_clk_o(clk_dac),
|
||
.dac_data(dac_data),
|
||
.dac_wrt(dac_wrt),
|
||
|
||
// ADC
|
||
.adc_clk_o(clk_adc),
|
||
.adc_data(adc_data),
|
||
.adc_otr(adc_otr)
|
||
);
|
||
//------------------------------------------------------------
|
||
// Тактовые сигналы
|
||
//------------------------------------------------------------
|
||
initial begin
|
||
forever #(CLK_REF_PERIOD/2) clk_ref = ~clk_ref;
|
||
end
|
||
initial begin
|
||
forever #(CLK_ETH_PHY_PERIOD/2) clk_eth_phy = ~clk_eth_phy;
|
||
end
|
||
//------------------------------------------------------------
|
||
// Таски для тестирования
|
||
//------------------------------------------------------------
|
||
// Таски работы с AXI-Stream
|
||
task automatic dut_soft_reset(virtual axis_if#(8).tb vif);
|
||
logic [7:0] tx_packet[];
|
||
tx_packet = '{8'h0f};
|
||
vif.master_send(tx_packet);
|
||
endtask
|
||
|
||
task automatic dut_start(virtual axis_if#(8).tb vif);
|
||
logic [7:0] tx_packet[];
|
||
tx_packet = '{8'hf0};
|
||
vif.master_send(tx_packet);
|
||
endtask
|
||
|
||
task automatic dut_send_system_config(
|
||
virtual axis_if#(8).tb vif,
|
||
input logic [31:0] pulse_width,
|
||
input logic [31:0] pulse_period,
|
||
input logic [15:0] pulse_num,
|
||
input logic [13:0] pulse_height, // achtung! p_height strictly must have 14 bits of width
|
||
input logic [31:0] pulse_period_adc
|
||
);
|
||
// Создаем временный фиксированный массив и упаковываем всё одной строкой
|
||
logic [7:0] tx_packet[];
|
||
|
||
tx_packet = '{
|
||
8'h88, // Команда
|
||
pulse_width[7:0], pulse_width[15:8], pulse_width[23:16], pulse_width[31:24],
|
||
pulse_period[7:0], pulse_period[15:8], pulse_period[23:16], pulse_period[31:24],
|
||
pulse_num[7:0], pulse_num[15:8], pulse_height[7:0], 8'({2'b00, pulse_height[13:8]}),
|
||
pulse_period_adc[7:0], pulse_period_adc[15:8], pulse_period_adc[23:16], pulse_period_adc[31:24]
|
||
};
|
||
|
||
vif.master_send(tx_packet);
|
||
endtask
|
||
|
||
// Таски сбора и обработки статистики
|
||
task automatic dut_read_output(
|
||
virtual axis_if#(8).tb vif,
|
||
input int sample_num,
|
||
input bit randomize_recv_delays,
|
||
output int output_data[]
|
||
);
|
||
logic [7:0] rx_packet[];
|
||
logic [ACCUM_WIDTH-1:0] data_packet[];
|
||
int packet_num = $ceil(real'(sample_num / WINDOW_SIZE) / real'(PACKET_SIZE));
|
||
int numbers_per_packet = PACKET_SIZE/(ACCUM_WIDTH/8);
|
||
int idx = 0;
|
||
|
||
if (sample_num % WINDOW_SIZE) begin
|
||
$display("[TB] -dut_read_output- Error, sample_num must be multiple of WINDOW_SIZE: %0d %% %0d = %0d", sample_num, WINDOW_SIZE, sample_num % WINDOW_SIZE);
|
||
$finish;
|
||
end
|
||
|
||
data_packet = new[numbers_per_packet];
|
||
output_data = new[numbers_per_packet * packet_num];
|
||
// count send_request posedge todo
|
||
// timeout todo
|
||
|
||
// recv loop
|
||
for (int i = 0; i < packet_num; i++) begin
|
||
// randomize_recv_delays todo
|
||
request_ready = 1;
|
||
vif.slave_recv(rx_packet);
|
||
request_ready = 0;
|
||
|
||
// unpack values
|
||
data_packet = {<< byte {rx_packet}};
|
||
data_packet = {<< ACCUM_WIDTH {data_packet}};
|
||
|
||
// copy and convert values
|
||
for (int j = 0; j < data_packet.size(); j++) begin
|
||
output_data[i * data_packet.size() + j] = int'(data_packet[j]);
|
||
end
|
||
end
|
||
|
||
// wait for dut.finish posedge todo
|
||
// timeout todo
|
||
|
||
// error handling todo
|
||
endtask
|
||
|
||
//------------------------------------------------------------
|
||
// ОСНОВНОЙ ПРОЦЕСС ТЕСТИРОВАНИЯ
|
||
//------------------------------------------------------------
|
||
initial begin
|
||
int output_data[];
|
||
|
||
automatic virtual axis_if.tb control_vif = axis_control_if.tb;
|
||
automatic virtual axis_if.tb accumulator_vif = axis_accumulator_if.tb;
|
||
|
||
$display("[TB] DUT initializaton");
|
||
// Инициализация
|
||
request_ready = 0;
|
||
rst_n = 0;
|
||
#100;
|
||
rst_n = 1;
|
||
wait(mmcm_locked === 1'b1);
|
||
#150;
|
||
$display("[TB] MMCM locked");
|
||
|
||
dut_soft_reset(control_vif);
|
||
#100;
|
||
|
||
// Тесты
|
||
$display("[TB] Tests start");
|
||
dut_send_system_config(
|
||
.vif(control_vif),
|
||
.pulse_width(32'd123),
|
||
.pulse_period(32'd5000),
|
||
.pulse_num(16'd1),
|
||
.pulse_height(14'd15000), // 0V
|
||
.pulse_period_adc(32'd2600)
|
||
);
|
||
#100;
|
||
dut_start(control_vif);
|
||
|
||
dut_read_output(
|
||
.vif(accumulator_vif),
|
||
.sample_num(2600),
|
||
.randomize_recv_delays(0),
|
||
.output_data(output_data)
|
||
);
|
||
|
||
$display("Received %0d numbers", output_data.size());
|
||
for (int i = 0; i < output_data.size(); i++) begin
|
||
$write("%0d ", output_data[i]);
|
||
end
|
||
$display("");
|
||
|
||
$display("[TB] ALL PASSED");
|
||
$finish;
|
||
end
|
||
endmodule
|