`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